webpack的核心原理
-
一切皆模块 正如js文件可以是一个‘模块’一样,其他的(如CSS,image,html)文件也可以视为模块。因此,你可以require('myJSfile.js')也可以require('.css')。这意味着我们可以将事物(业务)分割成更小的易于管理的片段,从而达到重复利用的目的。
-
按需加载
传统的模块打包工具(module bundlers)最终将所有的模块比编译成一个庞大的bundle.js文件。但是在真实的app里边,‘bundle.js’文件可能有10M-15M之大,而且异步加载部分代码以实现按需加载。
开发模式和生产模式
首先需要知道webapck有许多特性,一些是‘开发模式’下才有的,一些是‘生产模式’下才有的,还有一些是两种模式下都有的。
为了生成bundles文件,你可能在package.json文件加入如下的script项
'script':{
"build":"webpack --config webapck.config.prod.js"
"dev":"webapck-dev-server"
}
webpack CLI和webapck-dev-server
值得注意的是,webpack作为模块打包工具,提供两种用户交互接口
-
webapck cli tool --- 这是默认的接口(随着webpack的而存在于.bin目录中)
-
webapck-dev-server tool --- 一个node.js server(你需要单独安装它)
webapck CLI(非常适合做production build)
这个命令行工具通过cli接收一些配置option,或者通过config file来接受这些option(默认为webapck.config.js配置文件),这些配置选项将用于webpack的打包过程。
虽然你可能是通过cli来开始学习webapck的,但是实际上命令行工具更多用于创建生产环境下使用的bundle
用法如下:
OPTION 1:
//全局安装
npm install webpack --g
//在命令行使用时,使用webpack生成包
webpack
OPTION2:
//局部安装
npm install webpack --save
//将其添加至package.json的script下
“scripts”: {
“build”: “webpack --config webpack.config.prod.js -p”,
...
}
//使用方法
"npm run build"
webapck-dev-server(非常适合创建开发的build,并serve静态的assets)
webpack-dev-server是一个运行于8080的端口的express nodejs server,这个server会自己调用webpack本身实现构建,并且server构建出来的bundle,这个server的好处是他可以提供比如“Live Reloading”或者“Hot Module Replacement(HMR)”的实用功能
npm install webpack-dev-server --save
OPTION1:
//通过命令行使用
webpack-dev-server --inline --hot
OPTION2:
//添加至package.js的script
“scripts”: {
“start”: “webpack-dev-server --inline --hot”,
...
}
//使用
npm run start
open browser at:
http://localhost:8080
webpack vs webpack-dev-server options
注意像inline和hot这些选项时webapck-dev-server特有的,而另外的如hind-modules则是CLI模块特有的选项
wepack-dev-server CLI选项和配置项 另外需要注意的是你可以通过下面两种方式向webpack-dev-server传入参数:
- 通过webpack.config.js文件的‘devserver’对象
- 通过CLI选项
// 通过CLI传参
webpack-dev-server --hot --inline
// 通过webpack.config.js传参
devServer: {
inline: true,
hot:true
}
有时候devServer配置项(hot: true 和inline: true)不生效,所以,我更偏向于使用如下的方式向cli传递参数
// package.json
{
"scripts": "webpack-dev-server --hot --inline"
}
注意:确定没有同时传入hot: true 和-hot
webpack-dev-server的“hot” 和 “inline”选项
'inline'选项会为入口页面添加‘热加载’功能,‘hot’则开启‘热替换’,即尝试重新加载组件改变的部分(而不是重新加载整个页面)。如果两个参数都传入,当资源改变时,erbpack-dev-server将会先尝试热替换,如果失败则重新加载整个入口页面。
//当资源发生改变,以下三种情况会生成新的bundle,但是又有区别
// 不会刷新浏览器
webpack-dev-server
//刷新浏览器
webpack-dev-server --inline
//重新加载改变的部分,HRM失败则刷新页面
“entry”:值分别是字符串,数组和对象的情况
entry配置项告诉webpack应用的根模块或起始点在哪里,而且不同的类型有不同的目的。
像绝大多数app一样,倘若你的应用只有一个单一的入口,enter项的值你可以使用任意类型,最终输出的结果都是一样的
entry-array
但是,如果你想追加多个互不依赖的文件,那么可以使用array格式
比如:你可能需要‘googleAnalytics.js’到你的html中,那么你可以告诉webpack追加改analytics.js到bundle.js的后面
entry-object
现在,假设你的应用是多页面的(multi-page application)而不是SPA,有多个html文件(index.html和profile.html)。然后你通过一个对象告诉webpack为每一个html生成一个bundle文件
以下的配置将会生成两个js bundle文件,indexEntry.js和profileEntry.js分别应用于index.html和profile.html中
使用方法为:
//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:
output:‘path’项和‘publicPath’项
output项告诉webpack怎样存储输出结果以及存储到哪里。output的两个配置项‘path’和‘publicPath’可能会造成困扰。
‘path’仅仅是告诉webapck结果存储在哪里,然而‘publicPath’项则被许多webpack的插件用在生产模式下更新内嵌到css、html文件里的url值
例如:在你的css中,你可能又一个url从你的localhost来load'./test.png'。但是在生产环境下,test.png文件可能存在于CDN中,这意味着你可能需要手工的更新这些url以便在生产环境中可以之下那个正确的url地址
为了解决这个手工修改url的问题,你可以使用webpack重的publicPath参数,而这个参数对几乎所有的plugin都是能够理解并使用这个publicPath参数的,并且自动在创建production build是更新这些url
// 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来引入这些文件到js中
例如L你可以使用bable-loader来转换es6写的js代码到浏览器可以认识的es5代码
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(从右往左):链式(管道)的加载器 多个loader可以用在同一个文件上并且被链式调用。链式调用时从右到左执行且loader之间用“!”来分割
例如,我们如果有一个“macss.css”文件,我们需要将它的内容dump到html的css content中,我们可以通过一下两个loaders来实现这个功能:css-loader和style-loader
module: {
loaders: [{
test: /\.css$/,
loader: ‘style!css’ <--(short for style-loader!css-loader)
}]
下面这张图可以解释这个过程是如何工作的
-
首先webpack在module中检索相关依赖,webpack看到mycssfile.css通过require来做import,因此mycssfile.css将作为dependency来处理,webpack首先将该css文件给到'css-loader'来做处理
-
css-loader加载所有的css内容以及该css内容中自己的depency(比如@import othercss),并保存为json. webpack然后将这个结果传给style-loader继续处理。
-
style-loader则接收这个json,并且增加css contents并将这个字符串插入到index.html文件中
loaders也可以进行配置
loaders可以通过传入不同的patameters以不同的方式来工作
在下面的栗子中,我们配置url-loader当image小于1024字节的话,直接使用DataURLs。我们可以传入limit参数来指定这个大小
.babelrc文件
babel-loader使用“presets”配置选项使se6转为es5的过程,一级如何解析react的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”]
}
插件
插件一般都是用来输出bundle的node模块
例如,uglifyJSPlugin获取bundle.js然后压缩和混淆内容以减小文件体积。
类似的extract-text-webpack-plugin内部使用css-loader和style-loader来收集所有的css到一个地方最终将结果提取结果到一个独立的”styles.css“文件,并且在html里边引用style.css文件。
//webpack.config.js
// 获取所有的.css文件,合并它们的内容然后提取css内容到一个独立的”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
]
}
注意:如果你只是想把css使用style标签内联到html里,你不必使用extract-text-webpack-plugin,仅仅使用css loader和style loader即可:
module: {
loaders: [{
test: /\.css$/,
loader: 'style!css' // (short for style-loader!css-loader)
}]
加载器和插件
你可能已经意识到了,Loader处理单独的文件级别并且通常作用于包生成之前或生成的过程中。
而插件则是处理包(bundle)或者chunk级别,且通常是bundle生成的最后阶段。一些插件如commonschunkplugin甚至更直接修改bundle的生成方式。
处理文件的扩展名
很多Webpack的配置文件都有一个resolve属性,然后就像下面代码所示有一个空字符串的值。空字符串在此是为了resolve一些在import文件时不带文件扩展名的表达式,如require('./myJSFile')或者import myJSFile from './myJSFile'(译者注:实际就是自动添加后缀,默认是当成js文件来查找路径)
{
resolve: {
extensions: ['', '.js', '.jsx']
}
}
CDN相关内容
内容分发网络(Content Delivery Network,简称CDN)
是建立并覆盖在承载网之上,由分布在不同区域的边缘节点服务器群组成的分布式网络
假设通过CDN加速的域名为www.a.com, 接入CDN网络,开始使用加速服务后,当终端用户(北京)发起HTTP请求时,处理流程如下:
- 当终端用户(北京)向www.a.com 下的指定资源发起请求时,首先向LDNS(本地DNS)发起域名解析请求。
- LDNS检查缓存中是否有www.a.com 的IP地址记录。如果有,则直接返回给终端用户;如果没有,则向授权DNS查询。
- 当授权DNS解析www.a.com 时,返回域名CNAME www.a.tbcdn.com 对应IP地址。域名解析请求发送至阿里云DNS调度系统,并为请求分配最佳节点IP地址。 4.LDNS获取DNS返回的解析IP地址。用户获取解析IP地址。用户向获取的IP地址发起对该资源的访问请求。 工作原理:
- CDN的加速资源是跟域名绑定的
- 通过域名访问资源,首先是通过DNS分查找离用户最近的CDN节点(边缘服务器)的IP
- 通过IP访问实际资源是,如果CDN上并没有缓存资源,则会到源站请求资源,并缓存到CDN节点上,这样,用户下一次访问的时候,该CDN节点那就会有对应资源的缓存了。