本身配置过webpack的人应该都知道,webpack真的好复杂,一开始作项目都是拿别人现成的作作小修改,可是别人的终究没有本身配的舒服。因此我打算写这篇文章,从个人配置中带你们了解webpack配置,简化读webpack官方页面的复杂度。javascript
首先咱们须要明确咱们要求webpack作什么?css
1.所写即所得,咱们在编译器中写了代码能立刻呈如今调试器上(热更新服务)html
2.本地开发获取数据存在的跨域问题(代理,解决跨域)vue
3.使用es678,less,sass等(翻译,让浏览器懂得咱们的代码)java
4.提升项目性能,好比压缩代码,压缩图片等(项目优化)node
5.若是与输入相关的需求,找entry(好比多页面就有多个入口) 6.若是与输出相关的需求,找output(好比你须要定义输出文件的路径、名字等等) 7.若是与模块寻址相关的需求,找resolve(好比定义别名alias) 8.若是与转译相关的需求,找loader(好比处理sass处理es678N) 9.若是与构建流程相关的需求,找plugin(好比我须要在打包完成后,将打包好的文件复制到某个目录,而后提交到git上) 接下来咱们来看下webpack的一些基本配置react
1.entry(项目入口)webpack
2.output(出口文件)git
3.modules(模块处理)es6
4.plugin(插件)
5.resolve
6.devserver
7.mode,
8devtool
主要有三种方式
1.字符串形式
entry: '.src/main.js'
复制代码
2.数组形式
entry: [react, react-dom]
复制代码
3.对象形式
entry: {
main:'./src/index2.js',
second: './src/index2.js',
vendor:['react', 'react-dom']
}
复制代码
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'
}
};
复制代码
在上面的示例中,咱们经过 output.filename 和 output.path 属性,来告诉 webpack bundle 的名称,以及咱们想要 bundle 生成(emit)到哪里。 Path.resolve是什么,引入的path模块是干什么用的
Nodejs
该path.resolve()方法将一系列路径或路径段解析为绝对路径。
给定的路径序列从右到左处理,随后每一个path路径都被预先加载,直到构造出绝对路径。例如,给定路径段的序列:/foo,/bar,baz,调用path.resolve('/foo', '/bar', 'baz')将返回/bar/baz。
若是在处理path完全部给定段以后还没有生成绝对路径,则使用当前工做目录。 生成的路径已规范化,而且除非将路径解析为根目录,不然将删除尾部斜杠。 零长度path段被忽略。
若是没有path传递段,path.resolve()将返回当前工做目录的绝对路径。
path.resolve('/foo/bar', './baz');
// Returns: '/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/');
// Returns: '/tmp/file'
path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
// If the current working directory is /home/myself/node,
// this returns '/home/myself/node/wwwroot/static_files/gif/image.gif'
_dirname
复制代码
当前模块的目录名称。这是同样 path.dirname()的__filename。
示例:node example.js从中运行/Users/mjr
console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr
复制代码
咱们在配置output是经常使用的几个
filename 这个是输出文件的名称,字符串类型,若是只有一个输出文件,能够写成静态名称。例如
output:{
filename:'bundle.js'
}
复制代码
固然了,在咱们平常工做中,通常状况下是不会有这种状况的,当项目很大的时候,若是不分块打包,bundle.js会惊人的大,项目越大,bundle.js就会越大,这不是咱们今天讨论的重点,之后再说
多个chunk的时候怎么办呢
webpack会为每一个生成的Chunk取一个名称,Chunk的名称和Entry的配置有关:
然而,当经过多个入口起点(entry point)、代码拆分(code splitting)或各类插件(plugin)建立多个
bundle,应该使用如下一种替换方式,来赋予每一个 bundle 一个惟一的名称…… 使用入口名称:
output:{
filename: "[name].bundle.js"
}
复制代码
使用内部 chunk id
output:{
filename: "[id].bundle.js"
}
复制代码
使用每次构建过程当中,惟一的 hash 生成
output:{
filename: "[name].[hash].bundle.js"
}
复制代码
使用基于每一个 chunk 内容的 hash:
output:{
filename: "[chunkhash].bundle.js"
}
复制代码
这里多出来几个陌生词汇hash、chunkhash,它们是什么? hash、chunkhash和contenthash三者的区别 hash
hash的值是相同的,若是都使用hash的话,由于这是工程级别的,即每次修改任何一个文件,全部文件名的hash至都将改变。因此一旦修改了任何一个文件,整个项目的文件缓存都将失效。因此对于没有改变的模块而言,这样作显然不恰当,由于缓存失效了嘛。此时,chunkhash的用途随之而来。 chunkhash
只有被修改了的文件的文件名,hash值修改
filename: '[name]-[chunkhash].js'
当咱们使用mini-css-extract-plugin拆分css的时候,就须要使用chunkhash,我一个js文件里面引入了css文件。这时要是我修改了js,但没修改css,能够经过chunkhash缓存css文件
contenthash
对css使用了chunkhash以后,咱们测试会发现,若是修改了js,css文件名的hash值确实没变,但这时要是咱们修改css文件的话,咱们就会发现css文件名的chunkhash值竟然没变化,这样就致使咱们的非覆盖发布css文件失效了。因此这里须要注意就是css文件必须使用contenthash。
上面介绍的 id、name、hash、chunkhash等都是webpack内置变量, id是惟一标示,不会重复,从0开始, name 是模块名称,是你本身起的,在配置路由懒加载的时候能够本身命名 官网介绍的很清楚,我就再也不这里啰嗦了, chunkFilename
官网解释:此选项决定了非入口(non-entry) chunk 文件的名称, 什么场景须要呢?
在按需加载(异步)模块的时候,也就是路由懒加载,这样的文件是没有被列在entry中的,
好比
{
entry: {
"index": "pages/index.jsx"
},
output: {
filename: "[name].min.js",
chunkFilename: "[name].min.js"
}
}
const myModel = r => require.ensure([], () => r(require('./myVue.vue')), 'myModel')
复制代码
上面的例子,经过filename输出的是index.min.js 异步加载的模块是要以文件形式加载哦,因此这时生成的文件名是以chunkname配置的,经过chunkFilename输出的是myModel.min.js 因此chunkFilename也很重要哦!!! path path是配置输出文件存放在本地的目录,字符串类型,是绝对路径
output:{
path: path.resolve(__dirname, 'dist/assets')
}
复制代码
__dirname,这个昨天说过,能够回顾一下,就是当前文件所在的文件夹的名字 publicPath 对构建出的资源进行异步加载(图片,文件),该选项的值是以 runtime(运行时) 或 loader(载入时) 所建立的每一个 URL 为前缀。所以,在多数状况下,此选项的值都会以/结束。 默认值是一个空字符串 "",即相对路径,配置错误会致使404 简单说,就是静态文件托管在cdn上 举个栗子: 若是你这么配置:
output:{
filename:'[name]_[chunkhash:8].js',
publicPath:'https://www.qdtalk.com/assets/'
}
复制代码
打包编译后,html页面就是这样的
path 和publicPath都支持字符串模板
const path = require('path');
module.exports = {
entry: main: './src/main.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
};
复制代码
// webpack 配置
const path = require('path');
module.exports = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
};
复制代码
模块处理主要是对loader的配置
此处引用官网对loader的定义
loader 让 webpack 可以去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 能够将全部类型的文件转换为webpack可以处理的有效模块,而后你就能够利用webpack的打包能力,对它们进行处理。
本质上,webpack loader 将全部类型的文件,转换为应用程序的依赖图(和最终的 bundle)能够直接引用的模块。 注意,loader 可以 import 导入任何类型的模块(例如 .css 文件),这是 webpack 特有的功能,其余打包程序或任务执行器的可能并不支持。咱们认为这种语言扩展是有很必要的,由于这可使开发人员建立出更准确的依赖关系图。 在更高层面,在 webpack 的配置中 loader 有两个目标:
webpack.config.js
const path = require('path');
const config = {
output: {
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
}
};
module.exports = config;
复制代码
以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性:test 和 use。这告诉 webpack 编译器(compiler) 以下信息:
“嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时,在你对它打包以前,先使用 raw-loader 转换一下。” 重要的是要记得,在 webpack 配置中定义 loader 时,要定义在 module.rules 中,而不是 rules。然而,在定义错误时 webpack 会给出严重的警告。为了使你受益于此,若是没有按照正确方式去作,webpack 会“给出严重的警告”
在webpack中有许许多多的loader,此处我按照官方文档解释下和我理解的用法 loader 特性
import txt from './file.txt';
复制代码
val-loader 加载的模块必须使用如下函数接口,将 default export 导出为一个函数。
function answer () {
return {
code: 'module.exports = 42;'
}
};
module.exports = answer;
复制代码
url-loader
url-loader 功能相似于 file-loader,可是在文件大小(单位 byte)低于指定的限制时,能够返回一个 DataURL。 file-loader 将文件发送到输出文件夹,并返回(相对)URL(不会再对文件作处理) JSON
转换编译(Transpiling)
React开发过程当中咱们须要将jsx或者es6代码转译成es5代码。 咱们须要用到babel-loader
{
'test': /\.(js|jsx)$/, // babel 转换为兼容性的 js
'exclude': /node_modules/,
'loader': 'babel-loader',
'query': {
'presets': ['react', 'latest', 'stage-0', 'react-hmre']
},
'include': path.resolve(__dirname, '../client')
},
复制代码
注:exclude是不转换node_modules的代码,query === options 此处注意babel版本6和7设置上有区别。 若是引用错误会报错 模板(Templating)
样式
{
'test': /\.less$/,
'loader': ['style-loader', 'css-loader', {
'loader': 'less-loader',
'options': {
'javascriptEnabled': true
}
}]
},
复制代码
此处的执行顺序。less-loader -> css-loader -> style-loader 清理和测试(Linting && Testing)
框架(Frameworks)
核心重点(敲黑板)
做用:能够处理各类任务,从打包优化和压缩,一直到从新定义环境中的变量 loader不须要require. plugin须要
loader 被用于转换某些类型的模块,而插件则能够用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到从新定义环境中的变量。插件接口功能极其强大,能够用来处理各类各样的任务。
想要使用一个插件,你只须要 require() 它,而后把它添加到 plugins 数组中。多数插件能够经过选项(option)自定义。你也能够在一个配置文件中由于不一样目的而屡次使用同一个插件,这时须要经过使用 new 操做符来建立它的一个实例。 下面是我配置的用于开发环境的plugins实例
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 经过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件
const config = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
module.exports = config;
复制代码
'plugins': [
new webpack.optimize.OccurrenceOrderPlugin(), // 调整模块的打包顺序,用到次数更多的会出如今文件的前面
new webpack.DefinePlugin({ // DefinePlugin 容许建立一个在编译时能够配置的全局常量。
'process.env.NODE_ENV': JSON.stringify('development')
}),
new HtmlWebpackPlugin({ // HtmlWebpackPlugin简化了HTML文件的建立,以便为你的webpack包提供服务。这对于在文件名中包含每次会随着编译而发生变化哈希的 webpack bundle 尤为有用。 你可让插件为你生成一个HTML文件,使用lodash模板提供你本身的模板,或使用你本身的loader。
'hash': true,
'title': 'Demo',
'filename': 'index.html',
'template': path.resolve(__dirname, '../views/index.ejs'),
'inject': 'body'
}),
new webpack.HotModuleReplacementPlugin(), // 启用热更新
new webpack.NoEmitOnErrorsPlugin(), // 输出阶段遇到编译错误跳过
new webpack.NamedModulesPlugin(), // 当开启 HMR 的时候使用该插件会显示模块的相对路径,建议用于开发环境。
new webpack.ProgressPlugin(), // 输出构建进度
]
复制代码