初始webpack
webpack
是一个打包模块化js的工具,可以通过loader
转换文件,通过plugin
扩展功能。
webapck
会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
大致的有四个核心概念:
- 入口(entry)
- 输出(output)
- loader
- 插件(plugins)
本文以webpack4为例,带你认识认识他们。
entry 入口
{
// 多入口打包。 app1, app2代表着后面的[name]. 后面以app1单文件入口为例
entry: {
app1: path.resove(__dirname, './app1.js'),
app2: path.resove(__dirname, './app2.js'),
}
}
output 输出
```js
{
<!--
path:文件打包输出的绝对路径位置。
filename: 文件名字。
publicPath: 输出解析文件的目录,url 相对于 HTML 页面(html的script标签url: './static/app1.ad212311c12.js')
下面的配置输出如下: dist<static<app1.ad212311c12.js
-->
output: {
path: pat.resove(__dirname, './dist'),
filename: path.posix.join('static', "[name].[hash].js"),
publicPath: './'
}
}
```
注:path.posix
和 path.win32
,前者跨平台,后者只是win上
resolve
webpack
在启动后会从配置的入口模块触发找出所有依赖的模块,resolve
配置webpack如何寻找模块对应的文件
```js
<!--
alias: 配置项通过别名来把原来导入路径映射成一个新的导入路径.
extensions: 在导入语句没带文件后缀时,webpack会自动带上后缀去尝试访问文件是否存在.
-->
{
resolve: {
alias: {
' @': path.resolve(__dirname, '../src')
},
extensions: ['.js', '.vue', '.json']
}
}
```
loader
loader是一种打包的方案,webpack默认只识别js结尾的文件,不同的loader转化不同的模块。
- babel-loader(针对JS文件)
- style-loader(针对CSS文件)
- css-loader(针对CSS文件)
- postcss-loader(针对CSS文件)
- stylus-loader(针对CSS文件)
- sass-loader(针对CSS文件)
- url-loader(针对FILE文件)
- file-loader(针对FILE文件)
- vue-loader(针对vue文件)
babel-loader
: 转换es5语法。需配合babel-core使用.
版本:
babel-loader@7.x.x对应babel-core@6.x.x
,babel-loader@8.x.x对应babel-core@7.x.x
. 如:"babel-core": "^6.22.1"。"babel-loader": "^7.1.1",
在
Babel
执行编译的过程中,会从项目的根目录下的.babelrc
文件中读取配置。.babelrc
是一个json
格式的文件
理解 babel-polyfill 和 babel-runtime 及 babel-plugin-transform-runtime
- 语法转义器。主要对javascript最新的语法糖进行编译,并不负责转译javascript新增的api和全局对象。例如let/const就可以被编译,而includes/Object.assign等并不能被编译。常用到的转译器包有,babel-preset-env、babel-preset-es2015、babel-preset-es2016、babel-preset-es2017、babel-preset-latest等。在实际开发中可以只选用babel-preset-env来代替余下的,但是还需要配上javascirpt的制作规范一起使用,同时也是官方推荐。
- 主要负责转译javascript新增的api和全局对象,例如babel-plugin-transform-runtime这个插件能够编译Object.assign,同时也可以引入babel-polyfill进一步对includes这类用法保证在浏览器的兼容性
- jsx和flow插件,这类转译器用来转译JSX语法和移除类型声明的,使用Rect的时候你将用到它,转译器名称为babel-preset-react
npm install -D babel-preset-stage-2 babel-preset-env babel-polyfill babel-plugin-transform-runtime
// .bebelrc
{
"plugins": [
[
"transform-runtime",
{
"polyfill": false
}
]
],
"presets": [
[
"env",
{
"modules": false
}
],
"stage-2"
]
}
{
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,// 或者include
options: {
cacheDirectory: true, // 缓存
sourceMap: false
}
},
{
test: /\.vue$/,
use: ['vue-loader'] // 需配合vueLoaderPlugin使用
},
{
test: /\.css$/,
use: [
// 使用<style>将css-loader内部样式注入到我们的HTML页面.
'style-loader',
'css-loader',
// css添加兼容前缀
{
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')] // 必须配合autoprefixer插件使用,否则不起作用
}
}
]
},
{
test: /\.stylus$/,
use: [
'style-loader',
'css-loader'
{
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
},
'stylus-loader'
]
},
{
test: /\.(jpe?g|png|gif)$/i, //图片文件
use: [
{
loader: 'url-loader',
options: {
limit: 140,
fallback: {
loader: 'file-loader',
options: {
name: path.posix.join('static', 'image/[name].[hash].[ext]')
}
}
}
}
]
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
use: [
{
loader: 'url-loader',
options: {
limit: 102,
fallback: {
loader: 'file-loader',
options: {
name: path.posix.join('static', 'media/[name].[hash].[ext]')
}
}
}
}
]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
use: [
{
loader: 'url-loader',
options: {
limit: 1024,
fallback: {
loader: 'file-loader',
options: {
name: path.posix.join('static', 'fonts/[name].[hash].[ext]')
}
}
}
}
]
}
}
Plugins 插件
下面介绍常规打包需要用到的插件
- autoprefixer(配合postcss-loader使用)
- clean-webpack-plugin(删除dist文件夹)
- copy-webpack-plugin(复制static文件夹,不经过打包)
- cross-env(设置环境变量,实现跨平台)
- html-webpack-plugin(配置html模板)
- mini-css-extract-plugin(压缩css)
- webpack-merge
- webpack-dev-server
- vue-template-compiler
- compression-webpack-plugin(代码压缩)
{
plugins: [
<!--
为html文件中引入的外部资源如script、link动态添加每次compile后的hash,防止引用缓存的外部文件问题, 可以生成创建html入口文件,比如单页面可以生成一个html文件入口,配置N个html-webpack-plugin可以生成N个页面入口
-->
new HtmlWebpackPlugin({
filename: 'index1.html',
template: path.join(__dirname, 'index1.html'), //模板文件路径
inject: true // 如果设置为 true 或者 body,所有的 javascript 资源将被放置到 body 元素的底部,'head' 将放置到 head 元素中。
}),
new HtmlWebpackPlugin({
filename: 'index2.html',
template: path.join(__dirname, 'index2.html'),
inject: true
}),
// 不用添加其他参数, 具体配置自己百度
new CleanWebpackPlugin(),
<!--
将CSS提取为独立的文件。这个插件应该只用在 production 配置中,并且在loaders链中不使用 style-loader, 特别是在开发中使用HMR,因为这个插件暂时不支持HMR。
webpack4需要自己使用压缩器,可以使用 optimize-css-assets-webpack-plugin 插件
-->
new MiniCssExtractPlugin({
filename: path.posix.join('static/css', '[name].[hash].css')
}),
// 不能和optimize-css-assets-webpack-plugin一起使用,否则css不被压缩
new CompressionWebpackPlugin({
filename: '[dir][name].gz[ext][query]',
algorithm: 'gzip',
test: /\.(js|css|html)$/,
threshold: 10240, // 大小阀值
minRatio: 0.8
})
]
}
核心功能optimization
公共代码提取 ,webpack4弃用CommonsChunkPlugin,内置 optimization
作用: 提取被重复引入的文件,单独生成一个或多个文件,这样避免在多入口重复打包文件
配置项如下:
splitChunks
: 主要就是根据不同的策略来分割打包出来的bundle
`chunks`:async(默认),all(推荐),initial.
// 入口
import "./a.js" //同步加载
import ('./b.js') //异步加载
// b.js
import vue from "vue" //
acyns: 分割异步打包的代码,打包出b和vue两个chunk
all: 分割异步同步代码(需要定义新规则,将同步的代码打包)
{
splitChunks: {
chunks: 'all',
cacheGroups: {
a : {
name: 'vendor',
test: /node_modules/
}
}
}
}
initial: 同时打包异步同步,但是异步内部的引入将不再考虑,直接打包一起,会将vue和b的内容打在一起.
cacheGroups
: 自定义配置决定生成的文件,缓存策略
- test : 限制范围,正则匹配文件夹或文件
- name : 打包的chunks的名字
- priority : 优先级,多个分组冲突时决定把代码放在哪块
- enforce: 强制生成
- minSize 生成新的chunk的最小体积,默认30000B
- minChunks 被entry引入的次数,默认1(为1时,适合分离node_modules里的第三方库)
- automaticNameDelimiter 定义文件名称连接符,默认~
runtimeChunk
: 作用是将包含chunks映射关系的list单独从app.js里提取出来,因为每一个chunk的id基本都是基于内容hash出来的,所以你每次改动都会影响它,如果不把它提取出来的话,等于app.js每次都会改变,缓存就失效了。
runtimeChunk
: true, //runtimeChunk可以配置成true,single或者对象,用自动计算当前构建的一些基础chunk信息,类似之前版本中的manifest信息获取方式.
minimizer
: 可以自定义UglifyJsPlugin
和一些配置,默认的压缩为uglifyjs-webpack-plugin
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
{
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
drop_debugger: true,
drop_console: true //生产环境自动删除console
},
warnings: false
},
sourceMap: false,
parallel: true //使用多进程并行运行来提高构建速度。默认并发运行数:os.cpus().length - 1
}),
new OptimizeCssAssetsPlugin()
],
}
webpack3和webpack4打包后的文件分析
webpack3:
-
app.js:基本就是你实际编写的那个app.vue(.vue或.js?),没这个页面跑不起来.
-
vendor.js:vue-cli全家桶默认配置里面这个chunk就是将所有从node_modules/里require(import)的依赖都打包到这里,所以这个就是所有node_modules/下的被require(import)的js文件
-
manifest.js: 最后一个chunk,被注入了webpackJsonp的定义及异步加载相关的定义(webpack调用CommonsChunkPlugin处理后模块管理的核心,因为是核心,所以要第一个进行加载,不然会报错).
webpack4:
- app.js 同上
- vendor.js: 同上
- runtime.js: 用自动计算当前构建的一些基础chunk信息,类似之前版本中的manifest信息获取方式
具体差异, 后面再说