// 一个常见的`webpack`配置文件
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: __dirname + "/app/main.js", //已多次提及的唯一入口文件
output: {
path: __dirname + "/build", //打包后的文件存放的地方
filename: "bundle-[hash].js" //打包后输出文件的文件名
},
devtool: 'none',
devServer: {
contentBase: "./public", //本地服务器所加载的页面所在的目录
historyApiFallback: true, //不跳转
inline: true, //构建变化后自动刷新网页实现实时预览
hot: true
},
module: {
rules: [{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
}, {
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [{
loader: "css-loader",
options: {
modules: true,
localIdentName: '[name]__[local]--[hash:base64:5]'
}
}, {
loader: "postcss-loader"
}],
})
}
}
]
},
plugins: [
new webpack.BannerPlugin('123'),
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" //new 一个这个插件的实例,并传入相关的参数
}),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin("style.css")
]
};
注:“_dirname”是node.js的一个全局变量,指向当前执行脚本所在的目录。
几个核心概念。
- Entry: 入口,webpack执行构建的第一步将从Entry开始,可抽象成输入
- Module: 模块,在Webpack里一切皆模块,一个模块对应一个文件。Webpack会从配置的Entry开始递归找出所有依赖的模块。
- Chunk: 代码块,一个Chunk由多个模块组合而成,用于代码合并与分割。
- Loader: 模块转换器,用于将模块的原内容按照需求转换成新内容。项目中需要的每个Loader都需要安装。
- Plugin: 扩展插件,在Webpack构建流程中的特定时机注入扩展逻辑,来改变构建结果或做我们想要的事情。
- Output: 输出结果,在Webpack经过一系列处理并得出最终想要的代码后输出结果。
Webpack在启动后会从Entry里面配置的Module开始,递归解析Entry依赖的所有Module。每找到一个Module,就会根据配置的Lodaer去找出对应的转换规则,对Module进行转换后,再解析出当前Module依赖的Module。这些模块会以Entry为单位进行分组,一个Entry及其所有依赖的Module被分到一个组也就是一个Chunk。最后,Webpack会将所有Chunk转换成文件输出。在整个流程中,Webpack会在恰当的时机执行Plugin里定义的逻辑。
DevServer
使用DevServer,DevServer会启动一个HTTP服务器用于服务网页请求,同时会帮助启动Webpack,并接收Webpack发出的文件变更信号,通过WebSocket协议自动刷新网页做到实时预览。
执行打包任务
可以在package.json里对scripts对象进行设置即可。
{
"scripts": {
"start": "webpack-dev-server --open --hot --progress --colors --host localhost",
"build": "rm -rf ./dist && webpack --progress --colors"
},
}
npm的start命令是一个特殊的脚本名称,其特殊性表现在,在命令行中使用npm start就可以执行其对于的命令,如果对应的此脚本名称不是start,想要在命令行中运行时,需要这样用npm run {script name} 如 npm run build
--open --host localhost
//执行npm start后自动打开浏览器
resolve.alias && ProvidePlugin
有时候我们在项目中会需要频繁引入同一个路径的文件,如果需要引入的次数特别多,我们就得在每一次引入都要写一长串的地址,那么我们有没有什么方法可以偷点懒呢,我们可以通过调整webpack里的配置达到“偷一点小懒”的目的。
resolve.alias这个配置项相当于为文件目录配置一个别名
使用resolve.alias配置的用法如下
module.exports = {
entry:
{
main:'./main.js',
},
output: {
path:__dirname+'/dist',
filename: '[name].js'
},
resolve:{
//配置别名,在项目中可缩减引用路径
alias: {
vue$: 'vue/dist/vue.esm.js',
'@': resolve('src'),
'&': resolve('src/components'),
'api': resolve('src/api'),
'assets': resolve('src/assets')
}
},
plugins: [
]
};
这样配置后在使用的时候就可以直接
import http from '@/utils/http'
代替
import http from 'src/utils/http'
reslove.alias可以让我们不用重复的去写一长串的路径,但是在使用的时候还是得引入,如果我们连引入都懒得引入呢?webpack为我们提供了ProvidePlugin这个帮我们解决问题的插件
那么ProvidePlugin要怎么使用呢?
webpack.config.js
const webpack = require('webpack')
module.exports = {
entry:
{
main:'./main.js',
},
output: {
path:__dirname+'/dist',
filename: '[name].js'
},
resolve:{
//配置别名,在项目中可缩减引用路径
alias: {
}
},
plugins: [
//提供全局的变量,在模块中使用无需用require引入
new webpack.ProvidePlugin({
$config: [resolve(`src/data/config/${process.env.CONFIG_ENV}.env.js`), 'default'],
}),
]
};
使用的时候就可以寄直接将 $config作为一个全局变量来使用
编写一个plugin
plugin可以监听webpack处理过程中的关键事件,深度集成进webpack的编译器,可以说plugin的执行层面是整个构建过程。Plugin系统是构成webpack的主干,webpack自身也基于plugin系统搭建,webpack有丰富的内置插件和外部插件,并且允许用户自定义插件。
插件的组成部分
- 一个javaScript命名函数,并暴露出去
- 在插件函数的prototype上定义一个apply方法,注入complier对象
- 指定一个绑定到webpack自身的事件钩子
- 处理webpack内部实例的特定数据
- 功能完成后调用webpack提供的回调
function MyPlugin(options) {}
// 2.函数原型上的 apply 方法会注入 compiler 对象
MyPlugin.prototype.apply = function(compiler) {
// 3.compiler 对象上挂载了相应的 webpack 事件钩子 4.事件钩子的回调函数里能拿到编译后的 compilation 对象
compiler.plugin('emit', (compilation, callback) => {
...
})
}
// 1.独立的 JS 模块,暴露相应的函数
module.exports = MyPlugin
Compiler 和 Compilation
compiler对象和compilation对象是webpack插件开发中最重要的两个资源,那他们分别是什么呢?
compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。
compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。
compiler 对象
compiler 即 webpack 的编辑器对象,在调用 webpack 时,会自动初始化 compiler 对象,源码如下:
// webpack/lib/webpack.js
const Compiler = require("./Compiler")
const webpack = (options, callback) => {
...
options = new WebpackOptionsDefaulter().process(options) // 初始化 webpack 各配置参数
let compiler = new Compiler(options.context) // 初始化 compiler 对象,这里 options.context 为 process.cwd()
compiler.options = options // 往 compiler 添加初始化参数
new NodeEnvironmentPlugin().apply(compiler) // 往 compiler 添加 Node 环境相关方法
for (const plugin of options.plugins) {
plugin.apply(compiler);
}
...
}
compilation 对象
结合源码来理解下上面这段话,首先 webpack 在每次执行时会调用 compiler.run() (源码位置),接着追踪 onCompiled 函数传入的 compilation 参数,可以发现 compilation 来自构造函数 Compilation。
// webpack/lib/Compiler.js
const Compilation = require("./Compilation");
newCompilation(params) {
const compilation = new Compilation(this);
...
return compilation;
}
path.join()与path.resolve()的区别
path 是 node.js内置的package,用来处理路径
-
path.join([path1],[path2][,...])
用于连接路径,对参数里的路径片段就行拼接
-
path.resolve([from...][,to])
将to解析成绝对路径
path.resolve(_dirname, '/img')
console.log(_dirname) //当前文件所在文件夹的绝对路径