深入学习webpack(二)

在深入学习webpack(一)中,我通过一个例子介绍了webpack的基本使用方法,下面将更为系统的学习webpack的基本概念,对于一门技术的掌握我认为系统化还是很重要的,如果仅仅是一知半解,更为深入的地方了解不够,那么你就泯然众人矣。

  webpack的核心概念主要有四个: entry(入口)、output(出口)、loaders(加载器)、plugins(插件)。 下面我将逐一介绍。

Entry

  webpack会创建应用里所有依赖的图表,而最开始的图表就是所谓的入口文件。 入口文件会告诉webpack从何处开始并且根据依赖清楚如何打包。所以入口文件就是承接上下文的根文件或者说是你的app第一个使用的文件。

  在webpack中我们在 webpack.config.js 中的entry选项里定义入口文件,最简单的定义方式如下:

module.exports = {
entry: './path/to/my/entry/file.js'
};

  这个非常容易理解,不再赘述。下面进行更为详细的介绍。

单一入口配置

  webpack.config.js内容如下:

const config = {
entry: './path/to/my/entry/file.js'
}; module.exports = config;

  这和之前的配置是一样的,只是我们把导出的这个对象赋给了config对象之后才导出的。

  说明: 这个语法是虽简单的语法,它是下面语法的简写形式

const config = {
entry: {
main: './path/to/my/entry/file.js'
}
};

   What happens when you pass an array to entry? Passing an array of file paths to the entryproperty creates what is known as a "multi-main entry". This is useful when you would like to inject multiple dependent files together and graph their dependencies into one "chunk".  

当你希望快速建立一个单入口应用的webpack配置时,这是一个非常棒的选择。 然而,这个语法并没有太多的可扩展性。

 注意:这里的main是可以替换的,并且换成任意的名称都是可以编译成功的,但是对于多入口文件,这里的名称就对应于html的名称了,比如两个页面,a.html和b.html,那么在entry中的两个键就是a和b了。

对象语法配置

  webpack.config.js如下:

const config = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
}
};

  对象语法可能显得冗杂一些,但是对于定义的入口文件和出口文件这是最具扩展性的方式。

"Scalable webpack configurations" are ones that can be reused and combined with other partial configurations. This is a popular technique used to separate concerns by environment, build target and runtime. They are then merged using specialized tools like webpack-merge.

Scenarios

下面的入口文件的使用是它们在生产环境中使用的例子:

独立的App和Vender入口

webpack.config.js如下所示:

const config = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
}
};

从表面来看,它告诉webpack从app.js和verdors.js两者同时开始(作为入口文件), 这些图表是完全独立的, 在单页面应用中这是非常常见的(除了verdors)。

多页面应用

const config = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
};

与单页面不同,对于多页面显然就有多个入口文件,这样配置的目的就是如此,即告诉webpack我们需要三个独立的依赖图表。

Output

  即使你需要打包所有的文件到一个文件中你还是需要告诉webpack最终要打包到哪里,而这里的output属性就是告诉webpack如何处理打包文件的。

const path = require('path');

module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};

  同样注意:这里的path时node的内置模块。

  另外,出口文件的路径必须是绝对路径而不能是相对路径。

  即我们使用了output.filename和output.path属性来将所有文件最终打包到path和filename对应的文件中去,这样,在html中,我们就可以应用这些文件了。

  在entry中我们知道入口可以是多个,但是出口文件的配置确实指定的。 

  下面介绍一些常用的选项,即下面的属性是可以定义在output对象中的。

output.chunkFilename

  不常用

output.crossOriginLoding

  与跨域相关。

output.devtoolLineToLine

  不常用。

output.filename

  即指定出口文件的文件名,注意: 这里不能使用绝对路径,因为output.path选项决定了文件在磁盘上的位置,而filename仅仅是用来命名文件的。

 (1)单入口

{
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: __dirname + '/build'
}
} // writes to disk: ./build/bundle.js

  即对于单入口的文件,最终只会打包出一个文件。

 (2) 多入口

  当页面是多入口时,你应该使用替换法来确保每一个出口文件都有一个独一无二的名字。

  其中[name]将会被打包出的文件名所代替。

  [hash]将会被compilation(编译)所代替。

  [chunkhash]将会被chunk的hash代替。举例如下所示:

{
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/build'
}
} // writes to disk: ./build/app.js, ./build/search.js

  注意: 这里的app和search对应的页面是app.html和search.html两个。

output.hotUpdateChunkFilename

  不常用

output.hotUpdateFilename

  不常用

output.hotUpdateMainFilename

  不常用

output.jsonpFunction

  不常用

output.library

  不常用

output.libraryTarget

  不常用

output.path

  这必须是一个绝对路径。如下,config.js:

output: {
path: "/home/proj/public/assets",
publicPath: "/assets/"
}

  index.html:

<head>
<link href="/assets/spinner.gif"/>
</head>

  更加复杂的例子是使用CDN。那么config如下:

output: {
path: "/home/proj/cdn/assets/[hash]",
publicPath: "http://cdn.example.com/assets/[hash]/"
}

  注意: path和publicPath是不同的,前者是我们常用的方法,即将模块打包到的路径, 而publicPath是为了存放静态资源,如img等。

output.sourceMapFilename

  不常用

Loaders

  什么是Loaders?

Loaders are transformations that are applied on the source code of a module.

  为什么要用Loaders?

  因为比如我们使用es6的语法,但是现代浏览器不识别,所以在打包前我们就需要使用Loaders将他们进行编译,得到我们想要的结果。

  这样,通过webpack的Loaders,我们就不需要使用手工的方法去处理这些问题了。

将项目中所有的资源打包是webpack的职责而非浏览器的。 webpack认为所有的文件,包括css、html、scss、jpg等都是模块。然而,webpack只能识别JavaScript。

  webpack中的Loaders会把你的这些依赖像的文件文件转化为modules。

  Loaders的意图有两个:

  1. 通过test属性识别哪些文件应该被一个确定的Loader转化为模块。
  2. 通过use属性来转化这些文件,这样,转化后的模块就可以添加到你的依赖图表中了。

  如下所示:

const path = require('path');

const config = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{test: /\.(js|jsx)$/, use: 'babel-loader'}
]
}
}; module.exports = config;

  上面的配置文件中的modules下就定义了一个rules,且rules是一个数组,其中的每一个元素都是一个对象,配置了Loader。 其中,module是说这是我们所需要的模块。

  它实现了这样的一个功能:

"Hey webpack compiler, when you come across a path that resolves to a '.js' or '.jsx' file inside of a require()/import statement,

use the babel-loader to transform it before you add it to the bundle".

  即当webpack遇到了js和jsx后缀的文件时,就用babel-loader将其进行编译,然后再打包他们。

  非常重要的一点是当定义规则时,你是在module.rules下进行定义的,而不是在rules下定义的。

例子

  比如,我们可以使用loaders来告诉webpack加载一个css文件或者将Typescript转化为js,首先安装下面的loaders:

npm install --save-dev css-loader
npm install --save-dev ts-loader

  接下来,在webpack.config.js中配置: 即将所有的css文件都使用css-loader进行处理,ts同样。

module.exports = {
module: {
rules: [
{test: /\.css$/, use: 'css-loader'},
{test: /\.ts$/, use: 'ts-loader'}
]
}
};

  值得注意的是:就css-loader而言,下面的配置也是等效的: 

{test: /\.css$/, loader: 'css-loader'}
// or equivalently
{test: /\.css$/, use: 'css-loader'}
// or equivalently
{test: /\.css$/, use: {
loader: 'css-loader',
options: {}
}}

  

  另外,我们还可以对loader进行配置,配置的方式主要有三种:

  1. 通过webpack.config.js
  2. 在requires tatement中明确
  3. 通过CLI

通过 webpack.config.js

  module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader'},
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
}

这种方式是最简洁的方式,他可以用来帮助我们维护简洁的代码,并且提供了overview。

通过require

  通过require或者define来使用loader。 并且我们使用!来区分不同的loader, 每一部分都是相对于当前的目录。  

require('style-loader!css-loader?modules!./styles.css');

   并且我们可以通过在整个rule之前使用!来覆盖配置中的任何loader。

  options可以传递一个查询字符串,就像web中的一样?key=value&foo=bar。 使用一个JSON对象也是可以的。

  

通过CLI

webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'

  这里针对于jade文件使用了jade-loader,针对于css文件使用style-loader和css-loader,这种方式无疑非常麻烦的,不建议使用。

Loader的特性

  • Loaders可以是链式的,即链子上的第一个loader返回一个值作为此链子上的下一个loader需要处理的对象。
  • loaders可以是同步的,也可以是异步的。
  • loaders 可以运行在nodejs中。
  • loaders接受查询字符串。
  • loaders也可以通过options对象来配置。
  • plugins可以给loaders更多的特性。

Plugins

  loaders只能执行一些pre-file的转换,而plugins的功能更为强大,为了使用一个plugin,你必须require()到它, 

  然后再添加到plugins的数组中(一般都要new一个)。 

  注意: 因为你可能会多次使用同一个插件因为不同的意图,所以你需要每次都来new;而loader是针对所有的文件的,并且用途是固定的,所以没有new的概念。

  如下所示:

const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins
const path = require('path'); const config = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{test: /\.(js|jsx)$/, use: 'babel-loader'}
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
}; module.exports = config;

  即我们需要先通过npm install来安装到本地,然后在require得到,最后再在plugins中new一个。

  并且webpack的插件非常多,我们可以在这里查找更多的插件

插件剖析

  一个webpack插件就是一个JavaScript对象,它有一个apply属性,这个apply属性被webpack编译器调用,在整个编译的生命周期存在。

配置

  我们可以通过webpack.config.js进行配置,如下所示:

const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins
const path = require('path'); const config = {
entry: './path/to/my/entry/file.js',
output: {
filename: 'my-first-webpack.bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
loader: 'babel-loader'
}
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
}; module.exports = config;

https://webpack.js.org/concepts/plugins/

  

努力的人,运气永远都不会太差!

  

  

深入学习webpack(二)的更多相关文章

  1. 跟我一起学习webpack使用配置文件(二)

    接着跟我一起学习webpack(一)中的项目来,我们接下来使用配置文件 使用npx webpack -h 我们可以查看webpack的配置参数 从我们在package.json中添加的命令来看,当项目 ...

  2. crawler4j 学习(二)

    crawler4j 学习(二) 实现控制器类以制定抓取的种子(seed).中间数据存储的文件夹.并发线程的数目: public class Controller { public static voi ...

  3. 从零开始学习jQuery (二) 万能的选择器

    本系列文章导航 从零开始学习jQuery (二) 万能的选择器 一.摘要 本章讲解jQuery最重要的选择器部分的知识. 有了jQuery的选择器我们几乎可以获取页面上任意的一个或一组对象, 可以明显 ...

  4. Android Animation学习(二) ApiDemos解析:基本Animators使用

    Android Animation学习(二) ApiDemos解析:基本Animatiors使用 Animator类提供了创建动画的基本结构,但是一般使用的是它的子类: ValueAnimator.O ...

  5. AspectJ基础学习之二搭建环境(转载)

    AspectJ基础学习之二搭建环境(转载) 一.下载Aspectj以及AJDT 上一章已经列出了他的官方网站,自己上去download吧.AJDT是一个eclipse插件,开发aspectj必装,他可 ...

  6. WPF的Binding学习笔记(二)

    原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...

  7. AJax 学习笔记二(onreadystatechange的作用)

    AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...

  8. MyBatis学习系列二——增删改查

    目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring 数据库的经典操作:增删改查. 在这一章我们主要说明一下简单的查询和增删改, ...

  9. [译]开始学习webpack

    写在前面: 文章翻译自petehunt大神的petehunt/webpack-howto,作为学习webpack的开始.fork了一份,翻译后的在这里https://github.com/zjzhom ...

随机推荐

  1. docker网络模式----入门docker的难点

    众所周知,现在docker是轻量级虚拟化的典型代表!这段时间想要建立一个分布式系统,但是手头上主机没那么多,所以使用docker进行虚拟化,但是在使用的过程中对网络这一部分是一直不太理解,特别找了一篇 ...

  2. .net core 2.0 jwt身份认证系统

    经历了很久,.net core 2.0 终于发布了! 之前一直用的core 1.1,升级了2.0后发现认证的机制(Auth)发生了比较大的变化,在1.1中认证配置是在Configure中完成,而在2. ...

  3. eclipse jdk安装

    在Ubuntu16.04.4安装jdk 转载自:http://www.cnblogs.com/zyrblog/p/8510132.html 一.在Ubuntu16.04.4上安装jdk  1.下载jd ...

  4. Centos7 调整磁盘空间

    1. 查看磁盘空间占用情况:  df -h 可以看到 /home 有很多剩余空间, 而节点较少. 2. 备份 /home 下的内容: cp -r /home/ homebak/ 3. 关闭home进程 ...

  5. 51nod1445(最短路)

    题目链接: http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1445 题意: 中文题诶~ 思路: 可以将每种颜色看作一个节点 ...

  6. sublime安装插件autoprefixer

    安装插件autoprefixer步骤: 1.确保Node.js已经安装,未安装请 点击 这里>> 2.下载autoprefixer插件 https://github.com/sindres ...

  7. 为asp.net core 自定义路由动态修改

    根据IApplicationModelConvention 接口 实现相应的方法 /// <summary> /// 定义个类RouteConvention,private 来实现 IAp ...

  8. 树莓派编译安装 EMQ 服务器

    前言 EMQ 是一款开源的物联网 MQTT 消息服务器,使用 Erlang/OTP 语言平台设计,在 DIY 智能家居时可以作为网关,前几天摸索了一下在树莓派中安装 EMQ 的方法,记录一下. 步骤 ...

  9. 利用Android studio开发Java工程

    1. 新建项目 新建项目肯定是去new,但到底是new project还是new module是一个问题.在这解释一下,如果new project的话是新建一个工程,相当于新建一个工作区,工程中可以有 ...

  10. C语言中Extern用法

    extern用在变量或函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”. extern修饰变量的声明. 举例:若a.c中需引用b.c中的变量int v,可以在a.c中声明extern ...