webpack-易混淆部分的解释

时间:2023-09-25 17:32:38

原文链接:

https://medium.com/@rajaraodv/webpack-the-confusing-parts-58712f8fcad9

webpack的核心哲学

1. 任何皆模块

正如js文件可以是"modules",任何其他的文件,比如css, images, html都可以被视为modules。也就是说,你可以通过require("myJSfile.js")来加载js文件,也可以通过require("mycssFile.css")加载css文件。这也就意味着我们可以将任何工作成果视为更小的可管理的构建,可用于重用。

2. 仅仅在你需要使用的时候,仅仅加载你需要的asset. 典型的,打包器接收所有的模块输入而最终生成以恶搞巨大的单个"bundle.js"文件.但是很多真实的应用,这个bundel.js文件可能会达到10MB-15MB的大小!因此,这种情况下会导致加载非常慢。webpack为了处理这种bundle过大的问题,webpack提供了几个好用的功能用于split你的代码,并且产生出多个"bundle" files,并且可以async异步地加载parts of the app,以便仅仅加载你当前需要的部分。

下面我们来一个个探讨容易搞不清楚的webpack topic

Development vs production

首先我们要铭记在心的是webpack有非常朵的好功能,而这些功能中很大一部分仅仅是为"development-only"的功能,而一部分是"production-only"的,而剩下的部分是既可用于开发环境,又可用于生产环境。

webpack-易混淆部分的解释

典型地,大多数项目通过使用两个大的webpack config file来分别处理dev和prod两种场景。

要创建bundle,你可能会在package.json中这样写:

"scripts":{
// npm run build to build production bundles
"build": "webpack --config webpack.config.prod.js",
// npm run dev to generate development bundles and run dev server
"dev": "webpack-dev-server"
}

webpack CLI vs webpack-dev-server

webpack,作为打包工具,提供以下两个接口,理解这一点非常重要:

1. webpack cli tool---这是默认的接口(随着webpack的安装而存在于.bin目录中)

2. webpack-dev-server tool ---一个node.js server(你需要单独安装它)

webpack CLI(非常适合做prodcution build)

这个命令行工具通过cli接收一些配置option,或者通过一个config file来接收这些option(默认为webpack.config.js配置文件),这些配置选项将用于webpack的打包过程。

虽然你可能是通过使用cli来开始学习webpack的,但是实际上命令行工具往往更多用于创建生产环境下使用的bundle.

用法:

OPTION :
//Install it globally
npm install webpack --g
//Use it at the terminal
$ webpack //<--Generates bundle using webpack.config.js OPTION :
//Install it locally & add it to package.json
npm install webpack --save
//Add it to package.json's script
“scripts”: {
“build”: “webpack --config webpack.config.prod.js -p”,
...
}
//Use it by running the following:
"npm run build"

webpack-dev-server(非常适合创建开发的build,并serve静态的assets)

webpack-dev-server是一个运行于8080端口的expresss nodejs server.这个server会自己调用webpack本身实现构建,并且server构建出来的bundle.这个server的好处是它可以提供比如"Live Reloading"或者"Hot Module Replacement(HMR)"的实用功能。

npm install webpack-dev-server --save
//Use it at the terminal
$ webpack-dev-server --inline --hot
OPTION :
// Add it to package.json's script “scripts”: {
“start”: “webpack-dev-server --inline --hot”,
...
}
// Use it by running
$ npm start
Open browser at:
http://localhost:8080

webpack vs webpack-dev-server options

需要说明的是,比如"inline"或者"hot"这些配置选项仅仅是对webpack-dev-server来适用的。而比如"hide-modules"仅仅针对webpack cli来说是适用的。

webpack-dev-server CLI options vs config options

另外需要说明的一点是:你可以通过以下两种方式来传入webpack-dev-server配置选项options:

1. 通过webpack.config.js的"devServer对象

2.通过webpack-dev-server的CLI options传入:

//Via CLI
webpack-dev-server --hot --inline
//Via webpack.config.js
devServer: {
inline: true,
hot:true
}

我在测试中发现,有时通过devServer配置选项传入参数(hot:true, inline:true)并不总是能够工作,所以,我更喜欢通过在package.json中的cli option来传入对应的参数:

//package.json
{
scripts:
{“start”: “webpack-dev-server --hot --inline”}
}

"hot" vs "inline" webpack-dev-server options:

"inline"选项使得针对整个页面实现"live reloading"成为可能。"hot"选项使能了"hot module reloading"功能,而这个功能可以仅仅reload那些被变更过的component(而不是整个页面重新加载).如果我们同时传入两个配置选项,那么当source变化时,webpack-dev-server将会首先试图做HRM,如果HMR不work,则会reload整个页面。

//When the source changes, all 3 options generates new bundle but,

//1. doesn't reload the browser page
$ webpack-dev-server
//2. reloads the entire browser page
$ webpack-dev-server --inline
//3. reloads just the module(HMR), or the entire page if HMR fails
$ webpack-dev-server --inline --hot

"entry" --String Vs Array Vs Object

Entry这个配置项告诉webpack应用的root module或者starting point在哪里。这个配置项可以是string,可以是array,也可以是object.这个灵活性可能让我们搞得糊涂,但是这些不同类型的数据实际上是用于不同的目的的。

如果你有一个single starting point的话,那么string, array, object都是一样的结果。

webpack-易混淆部分的解释

entry-array

但是,如果你想追加多个互不依赖的文件,那么可以使用array格式

比如,你可能需要"googleAnalytics.js"到你的html中,那么你可以告诉webpack追加该analytics.js到bundle.js的后面:

webpack-易混淆部分的解释

entry-object

如果你有一个多页的web应用,有多个html文件(index.html, profile.html等),而不是一个SPA w/ multi-views,

那么你可以通过entry object告诉webpack需要一次性创建多个bundles。

下面的配置将产生两个js bundle文件:indexEntry.js和profileEntry.js分别应用于index.html和profile.html中

webpack-易混淆部分的解释

使用方法为:

//profile.html
<script src=”dist/profileEntry.js”></script>
//index.html
<script src=”dist/indexEntry.js”></script>

entry-combination

你也可以在object entry里面使用array entries。例如,下面的config将产生3个文件:vendor.js和index.js, profile.js:

webpack-易混淆部分的解释

output-"path" vs "publicPath"

output告诉webpack我们将最终的输出存放在哪里一级如何存放。这里两个参数:path和publicPath可能会产生歧义和误解。

"path"告诉webpack我们将最终的bundle存放在哪里。耳"publicPath"则被多个webpack plugin来使用用于当产生production build时更新在html,css文件中的url

webpack-易混淆部分的解释

例如,在你的css中,你可能有一个url从你的localhost来load './test.png‘。但是在生产环境下,test.png文件可能存在于CDN中。这意味着你可能需要手工的更新这些url以便在生产环境中可以指向到正确的url地址。

为了解决这个手工修改url的问题,你可以使用webpack的publicPath参数,而这个参数对几乎所有的plugin都是能够理解并使用这个publicPath参数的,并且自动在创建production build时更新这些url.

webpack-易混淆部分的解释

// Development: Both Server and the image are on localhost
.image {
background-image: url(‘./test.png’);
}
// Production: Server is on Heroku but the image is on a CDN
.image {
background-image: url(‘https://someCDN/test.png’);
}

loaders and chaning loaders

loaders是一些额外的node modules专门用于帮助'load'或者'import'各种类型的文件到浏览器可以认识的js, css文件格式。更进一步,loader也允许通过require或者import来import这些文件到js中。

例如:你可以使用bable-loader来转换es6写的js代码到浏览器可以认识的es5 javascript:

module: {
loaders: [{
test: /\.js$/, ←Test for ".js" file, if it passes, use the loader
exclude: /node_modules/, ←Exclude node_modules folder
loader: ‘babel’ ←use babel (short for ‘babel-loader’)
}]

chaining loaders(从右往左)

多个loaders可以级联起来,针对同一个文件做不同的转换。级联是从右往左工作的,同时使用" ! "来隔离

例如,我们如果有一个"mycssfile.css"文件,我们需要将它的内容dump到html的<style>css content</style>中。我们可以通过以下两个loaders来实现这个功能:css-loader和style-loader

module: {
loaders: [{
test: /\.css$/,
loader: ‘style!css’ <--(short for style-loader!css-loader)
}]

下面这张图可以解释这个过程是如何工作的:

webpack-易混淆部分的解释

1. 首先webpack在module中检索相关依赖,webpack看到mycssfile.css通过require来做import,因此mycssfile.css将作为dependency来处理,webpack首先将该css文件给到'css-loader'来做处理

2. css-loader加载所有的css内容以及该css内容中自己的depency(比如@import othercss),并保存为json. webpack然后将这个结果传给style-loader继续处理。

3. style-loader则接收这个json,并且增加<style>css contents</style>并将这个字符串插入到index.html文件中

loaders Themselves can be configured

loaders可以通过传入不同的parameters以不同的方式来工作。

在下面的例子中,我们配置url-loader当image小于1024字节的话直接使用DataURLs。我们可以传入limit参数来指定这个大小。

webpack-易混淆部分的解释

.babelrc文件

babel-laoder使用"presets"配置选项使能如何转换es6为es5的过程,一级如何解析react’s jsx 到js文件。我们可以通过query参数传入配置:

module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['react', 'es2015']
}
}
]
}

然而,很多项目中babel的配置项目可能很多,这种情况下,你就可以把balble的这些配置项目放到.babelrc文件中去。babel-loader将自动加载这个.babelrc文件如果它存在的话。

所以在很多例子中,你可能会看到:

//webpack.config.js
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel'
}
]
} //.bablerc
{
“presets”: [“react”, “es2015”]
}

Plugins

plugins是一些针对输出的bundle执行特别的工作的node modules.

例如,uglifyJSPlugin的作用是接收bundle.js作为输入并且混淆最小化该文件以减少文件的体积。

类似地,extract-text-webpack-plugin这个plugin则内部通过使用css-loader,style-loader来收集所有css内容并整合到一处并最终抽出这些css内容到一个单一的style.css文件中,并且在index.html中产生一个指向该style.css的link。

//webpack.config.js
//Take all the .css files, combine their contents and it extract them to a single "styles.css"
var ETP = require("extract-text-webpack-plugin"); module: {
loaders: [
{test: /\.css$/, loader:ETP.extract("style-loader","css-loader") }
]
},
plugins: [
new ExtractTextPlugin("styles.css") //Extract to styles.css file
]
}

需要说明的是,如果你想在html中inline使用这些css样式,你可以不使用该plugin,而仅仅通过使用css, style loaders达到目的:

module: {
loaders: [{
test: /\.css$/,
loader: ‘style!css’ <--(short for style-loader!css-loader)
}]

loaders vs plugins

loaders仅在bundle生成过程中或者生成之前针对单个的文件做转换。

而plugin则在bundle创建的结束之后针对整个bundle或者chunk level执行操作。一些像commonsChunksPlugin的插件则更进一步会修正bundles本身是如何创建的过程。

resolving file extensions

很多webpack config文件有一个resolve extensions属性,并有一个空字符串作为值。

这个空字符串用于帮助resolve imports without extensions,比如require("./myJSFile")或者import myJSFile从"./myJSFile”而不用加上文件的扩展名.

{
resolve: {
extensions: [‘’, ‘.js’, ‘.jsx’]
}
}