webpack工作原理:
通过一个入口文件,main.js开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个浏览器可识别的JavaScript文件。
Webpack的核心原理
1. 一切皆模块
正如js文件可以是一个“模块(module)”一样,其他的(如css、image或html)文件也可视作模 块。因此,你可以require('myJSfile.js')亦可以require('myCSSfile.css')。这意味着我们可以将事物(业务)分割成更小的易于管理的片段,从而达到重复利用等的目的。
2. 按需加载
传统的模块打包工具(module bundlers)最终将所有的模块编译生成一个庞大的bundle.js文件。但是在真实的app里边,“bundle.js”文件可能有10M到15M之大可能会导致应用一直处于加载中状态。因此Webpack使用许多特性来分割代码然后生成多个“bundle”文件,而且异步加载部分代码以实现按需加载。
最开始之前先说下:
1、package.json文件。 这个文件是npm的说明文件,创建方式是npm init,有了package.json文件以后就可以局部安装webpack模块作为依赖包了。(安装方式略)
package.json文件中的scripts中是一个对象,里面是{keyName : value}的方式,在node的环境下:npm run keyName 就能执行 value中的配置,所以这里可以把webpack的配置放到这里。
package.json中的脚本部分已经默认在命令前添加了node_modules/.bin
路径,所以无论是全局还是局部安装的Webpack,你都不需要写前面那指明详细的路径了
2、webpack.config.js文件 通常放在项目的根目录中,它本身也是一个标准的Commonjs规范的模块,具体的配置项通过module.exports = {}暴露出去,
module.exports = { entry : " ", /*入口输入项*/
output : {}, /*输出项*/
module : {/*加载器配置*/
loaders : [
{ test: / /, loader: ' ' } /*test是正则匹配的文件类型,loader是使用的加载资源loader*/
]
},
plugins : {},/*插件项*/
resolve : { /*其它解决方案配置*/
extensions : [],/*require的文件扩展名*/
alias : {},/*别名,用来缩写*/
root : "",/*查找module的绝对根路径*/
},
externals : {}/*项目中额外的类库或API*/
}
各个区域的备注:
1、var webpack = require('webpack');
// 不需解释,引入webpack,以后可以调用webpack的一些方法(需在devDependencies下安装webpack)。
2、var path = require('path');
// 这是node下面的Path模块(需在devDependencies下安装path),用于处理文件路径,因为js是前端脚本,很少去处理文件路径的问题,这个在后端语言中很常见(需掌握)。
******path下面有4个常用的方法******
path.join('参数1' ,'参数2') :用路径分隔符连接,它会自动识别系统,Unix系统是”/“,Windows系统是”\“。 path.join(mydir, "foo"); unix下----> 返回路径mydir/foo
path.resolve():最终结果,取出绝对路径。它可以接受多个参数,依次表示所要进入的路径,直到将最后一个参数(相当于cd进入每一个参数,最后调用cwd命令)
例子1: path.resolve('foo/bar', '/tmp/file/', '..', 'a/../subfile')
$ cd foo/bar
$ cd /tmp/file/
$ cd ..
$ cd a/../subfile
$ pwd(显示当前目录) 例子2:path.resolve('/foo/bar', './baz')
// '/foo/bar/baz'
例子3:
path.resolve('/foo/bar', '/tmp/file/')
// '/tmp/file'
例子4:path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif')
// 如果当前目录是/home/myself/node,返回
// /home/myself/node/wwwroot/static_files/gif/image.gif 结论:是 / 开头的绝对路径那么就替换成最新的 是 ./ 开头的相对路径就后面添加 是 ../ 开头的回退路径,就取消一层,最后输出所在的全路径名
path.relative() path.parse()略
3、process进程对象及其属性
process对象是Node的一个全局对象,可以在任意位置使用,不必通过require
命令加载。
process对象提供一系列属性 process.cwd() process.env _ _dirname
process.cwd()
与__dirname
的区别:前者进程发起时的位置的绝对路径(在node中代表所输命令行上面的那一行,
后者是脚本的位置(文件原原本本处于的目录位置),两者可能是不一致的。
process.env:process.env
属性返回一个对象,包含了当前Shell的所有环境变量,
一般这里的做法,新建一个环境变量NODE_ENV,用它确定当前所处的开发阶段。
生产阶段设为production
,开发阶段设为development。
用法:A 脚本中读取
process.env.NODE_ENV来确定是哪个阶段。B 运行脚本时,设置环境变量 node命令行中:
$ NODE_ENV=production node app.js
var env = process.env.NODE_ENV env === 'development' env === 'production'
4、webpack-dev-server依赖 支持2种自动刷新的方式 ——iframe模式和inline模式,默认监听8080端口
webpack-dev-server是一个Node.js服务器,一个小型的静态文件服务器(需要开发者从npm自行安装),为通过webpack打包生成的资源文件提供Web服务。
iframe模式:在网页中嵌入一个iframe
,将我们自己的应用注入到这个 iframe
当中去,因此每次你修改的文件后,都是这个 iframe
进行了 reload。
访问路径:
localhost:8080/webpack-dev-server/index.html。 ---------- 不推荐使用
inline模式:是 webpack-dev-server
会在你的 webpack.config.js
的入口配置文件中再添加一个入口
访问的路径是:localhost:8080/index.html ---------- 推荐使用
inline模式又分两种启动命令的方式:(Node.js API命令行方式和CLI模式(cmd line)):
1、CLI模式(又分两种写法):
写法a:在node窗口环境下输入,webpack需要全局安装,如果只是局部安装的话 node_modules/.bin/webpack
写法b:在package.json文件中的script中配置,用npm run来运行webpack命令
写法 1:
// 全局安装
npm install webpack-dev-server --save
// 运行
$ webpack-dev-server --inline --hot
写法 2:
// 添加到package.json scripts
"scripts": {
"start": "webpack-dev-server --inline --hot",
...
}
// 运行:
$ npm start
其中 --hot --inline为webpack-dev-server参数,有两种方式传参:
// 通过CLI传参
webpack-dev-server --hot --inline // 通过webpack.config.js文件的"devServer"对象
devServer: {
inline: true,
hot:true
}
webpack-dev-server的一些常用选项(用于开发dev时候,pro时候不用),可以写成 --形式,也可以在devServer对象中以key:value(boolean)形式来配置
// 当资源发生改变,以下三种方式都会生成新的bundle,但是又有区别: // 1. 不会刷新浏览器
$ webpack-dev-server //2. --inline“热加载”功能,刷新浏览器
$ webpack-dev-server --inline
//3. --hot “热替换,尝试重新加载组件改变的部分(而不是重新加载整个页面)
// --inline --hot 当资源改变时,webpack-dev-server将会先尝试HRM(即热替换),如果失败则重新加载整个入口页面
$ webpack-dev-server --inline --hot --color // 打包信息带有颜色显示 --no-colors // 关闭颜色 --quiet // 控制台中不输出打包的信息 --compress // 开启gzip压缩
--progress // 显示打包过程中的进度 --host // 配置主机名或IP, 0.0.0.0绑定到所有主机 --port // 配置端口号
--history-api-fallback // 是否支持HTML5历史API撤退,如果是true,则每次跳转都指向index.html
2、Node.js API命令行方式:
5、module.exports = { 配置项 } 配置项解析:
entry选项: webpack作用的入口文件,也就是启动webpack时候要最先处理的文件,一般这个文件内部会引用很多其他所依赖的模块。
此文件内部的写法也可以用import引入其他需要的模块,比如 import Vue from 'vue'; import VueRouter from 'vue-router';。
可以是字符串、数组或对象,
路径:相对webpack.config.js的路径,比如:此处的"./entry.js" 比如"./src/index.js"
路径也可以用path模块下的方法来配置:entry: path.resolve(__dirname, './src/app.js'),path.resolve()方法,可以结合我们给定的两个参数最后生成绝对路径,最终指向的就是我们
的app.js文件,此法要引入path模块,var path = require('path');
字符串:单一入口用字符串,不过单一入口用数组和对象也是可以的,无所谓
数组:单页面的多入口,且彼此不互相依赖的文件,使用数组格式
对象:多页面的多入口,有多个html文件,多处引用。以下的配置将会生成两个js文件:indexEntry.js和profileEntry.js分别会在index.html和profile.html中被引用。。
对象的混合使用:
output选项:
output输出文件,最终提交出去的经过合并压缩等等一系列操作的输出文件。
参数是个对象,定义了输出文件的位置path及名字filename和 publishPath 属性或者chunkFilename
path ---- 打包文件存放的绝对路径, 如果文件目录不存在那么自动创建
filename ---- 打包后的文件名,(如下例子)
publishPath ---- 代表静态资源发布后的前缀地址,即 网站运行时的访问路径(主要用于生产环境静态资源的重新配置),被许多Webpack的插件用于在生产模式下更新内嵌到css、html文件里的url值。
chunkFilename ---- 用来打包require.ensure方法中引入的模块,如果该方法中没有引入任何模块则不会生成任何chunk块文件
注意:对于不是在require.ensure方法中引入的模块,此属性不会生效,只能用CommonsChunkPlugin插件来提取
filename :当我们在entry中定义构建多个文件("./entry1", "./entry2")时,filename可以对应的更改为[name].js,用于定义不同文件构建后的名字。
举例:输入 page2: ["./entry1", "./entry2"],输出filename: "[name].bundle.js"
publishPath :在localhost(本地开发模式)里的css文件中边你可能用“./test.png”这样的url来加载图片,但是在生产模式下“test.png”文件可能会定位到CDN上并且你的Node.js服务器可能是运行在云平台上边的。这就意味着在生产环境你必须手动更新所有文件里的url为CDN的路径,有了这个选项配置,就简单多了。
下图是利用publishPath和url-loader在生产模式编译输出时候更改了url的路径(相对---->绝对)
// 开发环境:Server和图片都是在localhost(域名)下
.image {
background-image: url('./test.png');
}
// 生产环境:Server部署下HeroKu但是图片在CDN上
.image {
background-image: url('https://someCDN/test.png');
}
一些output的配置项。
module选项:
loaders : [{},{},{}] 对象组成的数组
module: {
loaders: [{
test: /\.js$/, // 匹配.js文件,如果通过则使用下面的loader (必选)
loader: 'babel' // 使用babel(babel-loader的简写)作为loader }] (必选)
exclude: /node_modules/, // 排除node_modules文件夹 ,不需要处理 (可选)
include: /node_modules/, // 手动添加必须处理的文件夹 (可选)
query: {} // 为loader提供额外的配置选项 (可选)
}]
}
test选项:
loader选项:将浏览器不能识别的类型,通过相应的loader模块加载器转换成可识别的代码。
规则:链式(管道式)的加载器(从右往左执行),需要多个loader的时候,从右往左执行,且不同的loader之间应该用!来分割开。
可简写,省略-loader字段,style-loader!css-loader可以写成style!css
举例:css-loader 处理css文件以及css的依赖(@import移入的css文件) style-loader 添加style标签嵌入到html中。
所以顺序就是:
- Webpack在模块颞部搜索在css的依赖项,即Webpack检查js文件是否有“require('myCssFile.css')”的引用,如果它发现有css的依赖,Webpack将css文件交给“css-loader”去处理
- css-loader加载所有的css文件以及css自身的依赖(如,@import 其他css)到JSON对象里,Webpack然后将处理结果传给“style-loader”,style-loader接受JSON值然后添加一个style标签并将其内嵌到html文件里
query:loader自身配置项(比如:针对图片的url配置)
配置url-loader来将小于1024字节的图片使用DataUrl替换而 大于1024字节的图片使用url,我们可以用如下两种方式通过传入“limit“参数来实现这一目的:
两种方式:(用参数模式或者query模式)
/*使用?参数的方式*/
{
test :/\.png$/,
loader: 'url-loader?limit=1024'
} /*使用query属性*/
{
test :/\.png$/,
loader: 'url-loader'
query:{
limit:1024
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
}
特殊的:
1、babel-loader的query配置项,他可以写到 loader[{}]中,也可以单出提出来写到一个 .babelrc文件中,webpack编译的时候会自动查找。
安装:babel-core babel-loader babel-preset-es2015
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modulesbower_components)/,
loader: 'babel',
query: { // 直接写到loader内部
presets: ['react', 'es2015']
}
}
]
}
或者
//webpack.config.js 文件 无query配置项 module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modulesbower_components)/,
loader: 'babel'
}
]
}
//.bablerc文件
{
presets: ['react', 'es2015']
}
.bablere文件的其他配置项解析
// .bablere文件的其他配置项解析
{
"presets": ["es2015", "stage-2"], // XXXXXXXXXXXXXXXXXXX
"plugins": ["transform-runtime"], // XXXXXXXXXXXXXX
"comments": false // XXXXXXXXXXXXXXXXXXX
}
2、PostCSS 安装postcss-loader 和 autoprefixer(自动添加前缀的插件)
直接写在module:{}中,而不是loader:[{}]中,和loader同级的。
module: {
loaders: [
{ test: /\.json$/, loader: "json" },
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel' },
{ test: /\.css$/, loader: 'style!css?modules!postcss' }
]
},
postcss: [
require('autoprefixer')//调用autoprefixer插件
],
devserver服务选项(对象):可以参考上面的
devServer: {
contentBase: "./public",//默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录(本例设置到“public"目录)
colors: true,//终端中输出结果为彩色
port : 8888,// 设置默认监听端口,如果省略,默认为”8080“
historyApiFallback: true,// 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html
inline: true// 当源文件改变时会自动刷新页面
}
devtool开发调试选项:
打包后的文件有时候你是不容易找到出错了的地方对应的源代码的位置的,Webpack在打包时可以为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法。
//配置生成Source Maps,选择合适的选项 // 在一个单独的文件中产生一个完整且功能完全的文件,减慢打包文件的构建速度
devtool: 'source-map',
// 在一个单独的文件中生成一个不带列映射的map,提高项目构建速度,但是只能对应到具体的行,不能对应到具体的列(符号)
devtool: 'cheap-module-source-map',
// 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。不过在开发阶段这是一个非常好的选项,但是在生产阶段一定不要用这个选项;
devtool: 'eval-source-map',
// 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射 (中小项目用)
devtool: 'cheap-module-eval-source-map',
plugins插件选项(数组):用new XX()的形式。。
plugins: [
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" // 命名模板文件名称为app下的index.tmpl.html,他到时候会自动被添加自动添加所依赖的 css, js,favicon等文件
}),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin("[name]-[hash].css")
]
HtmlWebpackPlugin // 这个插件的作用是依据一个简单的模板,帮你生成最终的Html5文件,这个文件中自动引用了你打包后的JS文件。每次编译都在文件名中插入一个不同的哈希值。
resolve选项(对象):
externals
webpack的一些插件:
WebPack.optimize.UglifyJsPlugin (WebPack内建插件)// 代码压缩
WebPack.optimize.CommonsChunkPlugin(WebPack内建插件)//提取公用文件,合并到一起
var HtmlWebpackPlugin = require('html-webpack-plugin'); //自动生成带hash的HTML 文件
var ExtractTextPlugin = require("extract-text-webpack-plugin"); // 独立样式文件,会将所有的样式文件打包成一个单独的style.css
参考:http://www.jianshu.com/p/dcb28b582318