不想错过更多好文?点击上面的 “CSS魔法” 订阅公众号。 |
仍在开发中……本文只反映截至 2.0.5-beta+ 版本的情况。
Webpack 2 将增加对 ES6 模块的原生支持。这意味着 Webpack 现在可以识别 import
和 export
了,不需要先把它们转换成 CommonJS 模块的格式:
import { currentPage, readPage } from "./book";
currentPage === 0;
readPage();
currentPage === 1;
// book.jsexport var currentPage = 0;
export function readPage() {
currentPage++;
}
export default "This is a book";
ES6 Loader 规范定义了 System.import
方法,用于在运行时动态加载 ES6 模块。
Webpack 把 System.import
作为拆分点,然后把被请求的模块放入一个单独的 “块”(chunk)中。
System.import
接受模块名作为参数,然后返回一个 Promise。
function onClick() {
System.import("./module").then(module => { module.default;
}).catch(err => { console.log("Chunk loading failed");
});
}
顺便说个好消息:chunk 加载失败产生的错误现在可以被捕获了。
还可以把一个表达式作为参数传给 System.import
。表达式的处理方式类似于 CommonJS(Webpack 为每个可能的文件创建一个独立的上下文)。
System.import
会令每个可能的模块都产生一个独立的 chunk。
function route(path, query) { return System.import("./routes/" + path + "/route")
.then(route => new route.Route(query));
}// This creates a separate chunk for each possible route// 这会为每种可能的路径组合都创建一个对应的 chunk。
至于 AMD 和 CommonJS,你可以自由地混用所有这三种模块类型(哪怕在是在同一个文件内)。Webpack 在这种情况下的行为跟 Babel 类似:
// CommonJS consuming ES6 Module// 以 CommonJS 语法调用 ES6 模块var book = require("./book");
book.currentPage;
book.readPage();
book.default === "This is a book";
// ES6 Module consuming CommonJS// ES6 模块调用 CommonJS 模块import fs from "fs"; // module.exports map to default
// module.exports 将映射为 defaultimport { readFileSync } from "fs"; // named exports are read from returned object+
// 具名导出的方法将可以通过模块返回的对象来读取typeof fs.readFileSync === "function";typeof readFileSync === "function";
在默认情况下,Babel 的 es2015
预设方案(preset)会把 ES6 模块转换为 CommonJS 格式。如果你想让 Webpack 来处理 ES6 模块,那你应该换用 es2015-webpack
这个预设方案。
ES6 模块与生俱来的静态特性允许我们采用一些新型的优化措施。比如说,在很多场景下,我们可以探测出哪些导出的接口会被用到,而哪些不会。
只要 Webpack 可以确定一个输出接口没有被别的模块用到,就会忽略那条输出语句。随后代码压缩工具就可以把那条声明标记为无用并丢弃。
在以下情况下,可以探测出接口的使用情况:
而在以下情况下,无法探测出接口的使用情况:
import * as ...
语句System.import
语句万一无法追踪导出接口的使用情况,Webpack 可以把导出的接口名混淆为单个字符的属性。(译注:抱歉,我不明白这句话。)
在过去的环境中,变量通常用来在配置文件中处理不同的环境。Webpack 2 引入了一种新方法,可以将选项传给配置。
配置文件可以导出一个函数,这个函数返回配置。这个函数会被 CLI(命令行界面)调用,而通过 --env
参数传过来的值会被传递给这个配置函数。
你可以传递一个字符串(--env dev
→ "dev"
),或者一个复杂的选项对象(--env.minimize --env.server localhost
→ {minimize: true, server: "localhost"}
)。我会推荐使用一个对象,因为那样更具扩展性,不过最后还是看你自己如何选择。
// webpack.config.babel.jsexports default function(options) { return { // ...
devtool: options.dev ? "cheap-module-eval-source-map" : "hidden-source-map"
};
}
解析器有少量重构( https://github.com/webpack/enhanced-resolve )。这意味着解析选项也发生了变化。基本上是一些简化,以及变得更加不容易写错。
新选项如下:
{
modules: [path.resolve(__dirname, "app"), "node_modules"] // (was split into `root`, `modulesDirectories` and `fallback` in the old options)
// In which folders the resolver look for modules
// relative paths are looked up in every parent folder (like node_modules)
// absolute paths are looked up directly
// the order is respected
// (以前这个选项分散在 `root`、`modulesDirectories` 和 `fallback` 三处。)
// 模块查找路径:指定解析器查找模块的目录。
// 相对路径会在每一层父级目录中查找(类似 node_modules)。
// 绝对路径会直接查找。
// 将按你指定的顺序查找。
descriptionFiles: ["package.json", "bower.json"], // These JSON files are read in directories
// 描述文件:这些 JSON 文件将在目录中被读取。
mainFields: ["main", "browser"], // These fields in the description files are looked up when trying to resolve the package directory
// 入口字段:在解析一个包目录时,描述文件中的这些字段所指定的文件将被视为包的入口文件。
mainFiles: ["index"] // These files are tried when trying to resolve a directory
// 入口文件:在解析一个目录时,这些文件将被视为目录的入口文件。
aliasFields: ["browser"], // These fields in the description files offer aliasing in this package
// The content of these fields is an object where requests to a key are mapped to the corresponding value
// 别名字段:描述文件中的这些字段提供了该包的别名对照关系。
// 这些字段的内容是一个对象,每当请求某个键名时,就会映射到对应的键值。
extensions: [".js", ".json"], // These extensions are tried when resolving a file
// 扩展名:在解析一个文件时,将会尝试附加这些文件扩展名。
enforceExtension: false, // If false it will also try to use no extension from above
// 强制使用扩展名:如果值为 false,在解析一个文件时,也会尝试匹配无扩展名的文件。
moduleExtensions: ["-loader"], // These extensions are tried when resolving a module
// 模块后缀名:在解析一个模块名时,将会尝试附加这些后缀名。
enforceModuleExtension: false, // If false it's also try to use no module extension from above
// 强制使用模块后缀名:如果值为 false,在解析一个模块名时,也会尝试匹配不包含后缀名的模块。
alias: {
jquery: path.resolve(__dirname, "vendor/jquery-2.0.0.js")
} // These aliasing is used when trying to resolve a module
// 另外:在解析一个模块名时,会使用这个别名映射表。}
Promise
的 polyfill分块加载机制现在是依赖于 Promise
的。这表示你需要在旧版浏览器下提供一个 Promise 的 polyfill。
ES6 规范已经包含了 Promise,我不想在每个打包文件中都加入一个 Promise 的 polyfill。因此,如果需要的话,就要由应用的开发者来提供这个 polyfill 了。
Promises 的兼容性情况 - CanIUse.com
你需要一个 Object.defineProperty
的 polyfill 来实现 ES6 的模块特性。或者在以(除了 module.exports
、module.id
、module.loaded
或 module.hot
之外的)其它方式使用 module
对象时,这个 polyfill 也是需要的。
为了实现 ES6 的模块特性,你还需要一个 Function.prototype.bind
的 polyfill。
最后这一条也不算新鲜了,但还是提一下吧:你需要一个 Object.keys
的 polyfill 来运行 require.context().keys()
。
现在,配置文件所指定的各个 loader 的值只匹配 resourcePath
(资源路径),而不是以前的 resource
(资源)。这表示 query string(查询字符串)不再参与匹配。
以前在使用 Bootstrap 时有一个问题,会把 Bootstrap 字体和图片的 test
字段搞复杂——必须把 /\.svg$/
写成 /\.svg($|\?)/
。现在你就可以直接使用那个简单的形式了。
配置文件所指定的各个 loader 现在是相对于配置文件进行解析的(但如果配置文件指定了 context
选项则以它为准)。在以前,如果某些外部模块是通过 npm link
链接到当前包的,则会产生问题,现在应该都可以解决了。
此外,我们将可以使用以下语法来配置 loader:
loaders: [
{
test: /\.css$/,
loaders: [ "style-loader",
{ loader: "css-loader", query: { modules: true } },
{
loader: "sass-loader",
query: {
includePaths: [
path.resolve(__dirname, "some-folder")
]
}
}
]
}
]
UglifyJsPlugin
将不再把所有 loader 都切到代码压缩模式。debug
选项已经被移除。Loader 不应该再从 Webpack 的配置那里读取自己选项了。取而代之的是,你需要通过 LoaderOptionsPlugin
来提供这些选项。
new webpack.LoaderOptionsPlugin({
test: /\.css$/, // optionally pass test, include and exclude, default affects all loaders
// 可以传入 test、include 和 exclude,默认会影响所有的 loader
minimize: true,
debug: false,
options: { // pass stuff to the loader
// 这里的选项会传给 loader
}
})
决定使用这种设计是出于 “关注点分离” 的考虑。我希望在配置中禁止随意的字段名,以便让校验配置成为可能。
现在许多插件将可以接受一个选项对象,而不是以前多个参数的形式。作为这个改动是因为这样更易于扩展。如果把旧版参数传给插件,插件会抛出一个错误。
在 Webpack 1 中,更新信号用的是 Web Messaging API(postMessage
)。而 Webpack 2 将使用一个标准的事件触发器来传递事件信号。这表示 WebSocket 必须内联到打包文件中。
webpack-dev-server 现在在默认情况下就处于内联模式。
这应该使得我们可以用 webpack-dev-server 来更新 WebWorker 中的代码。
OccurrenceOrderPlugin
这个插件将不再需要了,类似的功能默认就是开启的。
require.ensure
和 AMD 的 require
的加载方式现在都是异步的了,哪怕所需的 chunk 已经加载了。
如果微信无法正常显示代码,请点击 “阅读原文”。 |