webpack.config.js
参考链接:2020年了,再不会webpack敲得代码就不香了(近万字实战)
const path = require('path')
#每次执行npm run build会发现dist文件夹里会残留上次打包的文件,这里我们推荐一个plugin来帮我们在打包输出前清空文件夹clean-webpack-plugin
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
#html-webpack-plugin---webpack打包生成的js文件会被自动引入html文件中
#生成多个html-webpack-plugin实例来解决多入口文件开发的问题
const HtmlWebpackPlugin = require('html-webpack-plugin')
#vue-loader 用于解析.vue文件
#vue-template-compiler 用于编译模板
const vueLoaderPlugin = require('vue-loader/lib/plugin')
#webpack4.0以后,官方推荐使用mini-css-extract-plugin插件来打包css文件
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
#process 对象是一个 global (全局变量),提供有关信息,控制当前 Node.js 进程。作为一个对象,它对于 Node.js 应用程序始终是可用的,故无需使用 require()。
#process是node全局属性,直接用就行
#process.env属性返回一个包含用户环境信息的对象。#常用process.env.NODE_ENV来设置环境变量。在不同环境的机器上设置不同的 NODE_ENV,当然这个字段也不一定。你也可以换成其他的NODE_ENV_NIZUISHUAI等等,反正是自定义的。参考链接:[Node环境变量 process.env 的那些事儿](https://segmentfault.com/a/1190000011683741)
#process.argv 属性会返回一个数组,其中包含当 Node.js 进程被启动时传入的命令行参数。 第一个元素是 process.execPath。 如果需要访问 argv[0] 的原始值,则参见 process.argv0。 第二个元素是正被执行的 JavaScript 文件的路径。 其余的元素是任何额外的命令行参数。
#process.execPath 属性返回启动 Node.js 进程的可执行文件的绝对路径名。比如'/usr/local/bin/node'
const devMode = process.argv.indexOf('--mode=production') === -1;#判断当前是否是dev环境
module.exports = {
#entry: ["@babel/polyfill",path.resolve(__dirname,'../src/index.js')], // 入口文件
entry:{
main:path.resolve(__dirname,'../src/main.js')
},
output:{
path:path.resolve(__dirname,'../dist'),#打包后的目录
filename:'js/[name].[hash:8].js',#打包后的文件名称
chunkFilename:'js/[name].[hash:8].js'
},
module:{
rules:[
{
#用babel转义js文件 # npm i -D babel-loader @babel/preset-env @babel/core
test:/\.js$/,
use:{
#babel-loader只会将ES6/7/8语法转换为ES5语法,但是对新api并不会转换 例如(promise、Generator、Set、Maps、Proxy等)此时我们需要借助babel-polyfill来帮助我们转换(用法可看entry配置示例)
loader:'babel-loader',#当写法为babel-loader?cacheDirectory=true时,表示可以将Babel编译过的文件缓存起来,下次只需要编译更改过的代码文件即可,可以大幅度加快打包时间
options:{
presets:['@babel/preset-env',{ modules: false }]#因为Babel的预案(preset)默认会将任何模块类型都转译成CommonJS类型,这样会导致tree-shaking失效。修正这个问题也很简单,在.babelrc文件或在webpack.config.js文件中设置modules: false就好了
}
},
exclude:/node_modules/#配置include exclude也可以减少webpack loader的搜索转换时间。
},
{
test:/\.vue$/,
use:[{
loader:'vue-loader',
options:{
compilerOptions:{
preserveWhitespace:false
}
}
}]
},
{
test:/\.css$/,
use:[{
loader: devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
options:{
publicPath:"../dist/css/",
hmr:devMode
}
},'css-loader',{
loader:'postcss-loader',
options:{
plugins:[require('autoprefixer')]
}
}]#遵循从右向左解析原则
},
{
test:/\.less$/,
use:[{
loader:devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
options:{
publicPath:"../dist/css/",
hmr:devMode
}
},'css-loader','less-loader',{
#为css添加浏览器前缀
loader:'postcss-loader',
options:{
plugins:[require('autoprefixer')]
}
}]
},
#打包 图片、字体、媒体、等文件
#file-loader就是将文件在进行一些处理后(主要是处理文件名和路径、解析文件url),并将文件移动到输出的目录中
#url-loader 一般与file-loader搭配使用,功能与 file-loader 类似,如果文件小于限制的大小。则会返回 base64 编码,否则使用 file-loader 将文件移动到输出的目录中
{
test:/\.(jep?g|png|gif)$/,
use:{
loader:'url-loader',
options:{
limit:10240,
fallback:{
loader:'file-loader',
options:{
name:'img/[name].[hash:8].[ext]'
}
}
}
}
},
{
test:/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
use:{
loader:'url-loader',
options:{
limit:10240,
fallback:{
loader:'file-loader',
options:{
name:'media/[name].[hash:8].[ext]'
}
}
}
}
},
{
test:/\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
use:{
loader:'url-loader',
options:{
limit:10240,
fallback:{
loader:'file-loader',
options:{
name:'media/[name].[hash:8].[ext]'
}
}
}
}
}
]
},
#关于resolve的具体用法,参考链接:[webpack学习笔记--配置resolve](https://www.cnblogs.com/joyco773/p/9049760.html)
resolve:{
#当我们代码中出现 import 'vue'时, webpack会采用向上递归搜索的方式去node_modules 目录下找。为了减少搜索范围我们可以直接告诉webpack去哪个路径下查找。也就是别名(alias)的配置。
alias:{
'vue$':'vue/dist/vue.runtime.esm.js',
' @':path.resolve(__dirname,'../src')
},
#webpack会根据extensions定义的后缀查找文件(频率较高的文件类型优先写在前面)
extensions:['*','.js','.json','.vue']
},
plugins:[
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template:path.resolve(__dirname,'../public/index.html')#打包后生成的js文件会被自动引入html文件中
}),
new vueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: devMode ? '[name].css' : '[name].[hash].css',
chunkFilename: devMode ? '[id].css' : '[id].[hash].css'
})
]
}
webpack.dev.js(开发环境配置文件)
mode
可设置development production
两个参数
如果没有设置,webpack4
会将 mode
的默认值设置为 production
。
production
模式下会进行tree shaking
(去除无用代码)和uglifyjs
(代码压缩混淆)
const Webpack = require('webpack')
const webpackConfig = require('./webpack.config.js')
const WebpackMerge = require('webpack-merge')
module.exports = WebpackMerge(webpackConfig,{
mode:'development',
devtool:'cheap-module-eval-source-map',
#配置webpack-dev-server进行热更新 npm i -D webpack-dev-server
devServer:{
port:3000,
hot:true,
contentBase:'../dist'
},
plugins:[
new Webpack.HotModuleReplacementPlugin()
]
})
webpack.prod.js(生产环境配置文件)
#生产环境主要实现的是压缩代码、提取css文件、合理的sourceMap、分割代码
#需要安装以下模块:
#npm i -D webpack-merge copy-webpack-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin
const path = require('path')
const webpackConfig = require('./webpack.config.js')
const WebpackMerge = require('webpack-merge')#合并配置
const CopyWebpackPlugin = require('copy-webpack-plugin')#拷贝静态资源
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')#压缩css
#webpack mode设置production的时候会自动压缩js代码。原则上不需要引入uglifyjs-webpack-plugin进行重复工作。但是optimize-css-assets-webpack-plugin压缩css的同时会破坏原有的js压缩,所以这里我们引入uglifyjs进行压缩
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')#压缩js
module.exports = WebpackMerge(webpackConfig,{
mode:'production',
devtool:'cheap-module-source-map',
plugins:[
new CopyWebpackPlugin([{
from:path.resolve(__dirname,'../public'),
to:path.resolve(__dirname,'../dist')
}]),
],
optimization:{
minimizer:[
#或者使用webpack-parallel-uglify-plugin 增强代码压缩
new UglifyJsPlugin({//压缩js
cache:true,
parallel:true,
sourceMap:true
}),
new OptimizeCssAssetsPlugin({})
],
splitChunks:{
chunks:'all',
cacheGroups:{
libs: {
name: "chunk-libs",
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: "initial" // 只打包初始时依赖的第三方
}
}
}
}
})
优化webpack配置
1.优化打包速度(构建速度指的是我们每次修改代码后热更新的速度以及发布前打包文件的速度。)
- 合理的配置mode参数与devtool参数
- 缩小文件的搜索范围(配置include exclude alias noParse extensions)
- 使用HappyPack开启多进程Loader转换
npm i -D happypack
- 受限于Node是单线程运行的,所以Webpack在打包的过程中也是单线程的,特别是在执行Loader的时候,长时间编译的任务很多,这样就会导致等待的情况
- HappyPack可以将Loader的同步执行转换为并行的,这样就能充分利用系统资源来加快打包效率了
module:{ loaders:[{ test:/\.js$/, include:[resolve('src')], exclude:/node_modules/, loader:'happypack/loader?id=happybabel' }] }, plugins:[ new HappyPack({ id:'happybabel', loaders:['babel-loader?cacheDirectory'], threads:4#开启4个线程 }) ]
- 使用webpack-parallel-uglify-plugin 增强代码压缩
npm i -D webpack-parallel-uglify-plugin
- 抽离第三方模块(使用webpack内置的DllPlugin DllReferencePlugin进行抽离)
#对于开发项目中不经常会变更的静态依赖文件。类似于我们的elementUi、vue全家桶等等。因为很少会变更,所以我们不希望这些依赖要被集成到每一次的构建逻辑中去。
#这样做的好处是每次更改我本地代码的文件的时候,webpack只需要打包我项目本身的文件代码,而不会再去编译第三方库。以后只要我们不升级第三方包的时候,那么webpack就不会对这些库去打包,这样可以快速的提高打包的速度。
- 配置缓存
#我们每次执行构建都会把所有的文件都重复编译一遍,这样的重复工作是否可以被缓存下来呢,答案是可以的,目前大部分 loader 都提供了cache 配置项。
#比如在 babel-loader 中,可以通过设置cacheDirectory 来开启缓存,babel-loader?cacheDirectory=true 就会将每次的编译结果写进硬盘文件(默认是在项目根目录下的node_modules/.cache/babel-loader目录内,当然你也可以自定义)
#但如果 loader 不支持缓存呢?我们也有方法,我们可以通过cache-loader ,它所做的事情很简单,就是 babel-loader 开启 cache 后做的事情,将 loader
#的编译结果写入硬盘缓存。再次构建会先比较一下,如果文件较之前的没有发生变化则会直接使用缓存。使用方法如官方 demo 所示,在一些性能开销较大的 loader 之前添加此 loader即可。
- 如果你确定一个文件下没有其他依赖,就可以使用module.noParse属性让Webpack不扫描该文件,这种方式对于大型的类库很有帮助
2.优化打包文件体积 - 引入webpack-bundle-analyzer分析打包后的文件
- externals
- Tree-shaking(tree-shaking的主要作用是用来清除代码中无用的部分。)
- scope Hoisting:它会分析出模块间的依赖关系,尽可能的把打包出来的模块合并到一个函数中去
module.exports={ optimization:{ concatenateModules:true } }
生产环境中删除console.log
参考链接:vuecli3+webpack4优化实践(删除console.log和配置dllPlugin)
#可以通过配置webpack4中的terser-webpack-plugin插件达成目的,compress参数配置如下:
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
warnings: false,
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log']
},
},
}),
],
},
};
#在 vue.config.js 中的 configureWebpack 选项提供一个对象会被 webpack-merge 合并入最终的 webpack配置,因此vue-cli3构建的项目中只需要修改terserOptions即可,vue.config.js配置如下:
module.exports = {
publicPath: '/',
outputDir: 'dist',
devServer: {
port: 8080,
https: false,
hotOnly: true,
disableHostCheck: true,
open: true,
},
productionSourceMap: false, // 生产打包时不输出map文件,增加打包速度
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer[0].options.terserOptions.compress.warnings = false
config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
config.optimization.minimizer[0].options.terserOptions.compress.drop_debugger = true
config.optimization.minimizer[0].options.terserOptions.compress.pure_funcs = ['console.log']
}
}
}
#配置完成后使用 vue inspect --mode=production > output.js 命令审查项目的 webpack 配置
webpack魔术注释:
- 魔术注释是由 Webpack 提供的,可以为代码分割服务的一种技术。通过在 import 关键字后的括号中使用指定注释,我们可以对代码分割后的 chunk 有更多的控制权
import (
/* webpackChunkName: “my-chunk-name” */
/* webpackMode: lazy */#webpackMode 的默认值为 lazy 它会使所有异步模块都会被单独抽离成单一的 chunk,若设置该值为 lazy-once,Webpack 就会将所有带有标记的异步加载模块放在同一个 chunk 中。
'./someModule'
)
#通过添加 webpackPrefetch 魔术注释,Webpack 令我们可以使用与 <link rel=“prefetch”> 相同的特性。让浏览器会在 Idle 状态时预先帮我们加载所需的资源,善用这个技术可以使我们的应用交互变得更加流畅。
import(
/* webpackPrefetch: true */
'./someModule'
)
使用vue-cli时,Vue.config.js 配置选项
参考链接:vue.config.js 配置
module.exports = {
publicPath: './',#基本路径
outputDir: 'dist', #构建时的输出目录
assetsDir: 'static', #放置静态资源的目录
indexPath: 'index.html', #html的输出路径#指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径。
filenameHashing: true, #文件名哈希
pages: { #用于多页配置,默认是 undefined
index: {
entry: 'src/index/main.js', # page 的入口文件
template: 'public/index.html', # 模板文件
filename: 'index.html', # 在 dist/index.html 的输出文件
title: 'Index Page', # 当使用页面 title 选项时, # template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
chunks: ['chunk-vendors', 'chunk-common', 'index'] # 在这个页面中包含的块,默认情况下会包含 # 提取出来的通用 chunk 和 vendor chunk。
},
subpage: 'src/subpage/main.js' # 当使用只有入口的字符串格式时, # 模板文件默认是 `public/subpage.html` # 如果不存在,就回退到 `public/index.html`。 # 输出文件默认是 `subpage.html`。
},
lintOnSave: true, #是否在保存的时候使用 `eslint-loader` 进行检查。
runtimeCompiler: false,#是否使用带有浏览器内编译器的完整构建版本
transpileDependencies: ['resize-detector'], #babel-loader 默认会跳过 node_modules 依赖。
productionSourceMap: true, # 是否为生产环境构建生成 source map?
crossorigin: '', # 设置生成的 HTML 中 <link rel="stylesheet"> 和 <script> 标签的 crossorigin 属性。
integrity: false, # 在生成的 HTML 中的 <link rel="stylesheet"> 和 <script> 标签上启用Subresource Integrity (SRI)
configureWebpack: () => {}, #(Object | Function)#如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中#如果这个值是一个函数,则会接收被解析的配置作为参数。该函数及可以修改配置并不返回任何东西,也可以返回一个被克隆或合并过的配置版本。
chainWebpack: () => {}, #允许对内部的 webpack 配置进行更细粒度的修改。
# 配置 webpack-dev-server 行为。
devServer: {
open: process.platform === 'darwin',
host: '0.0.0.0',
port: 8080,
https: false,
hotOnly: false,
# 查阅 https:#github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/cli-service.md#配置代理
proxy: {
'/api': {
target: 'http:#app.rmsdmedia.com',
changeOrigin: true,
secure: false,
pathRewrite: {
'^/api': ''
}
},
'/foo': {
target: '<other_url>'
}
},
before: app => {}
},
# CSS 相关选项
css: {
# 将组件内的 CSS 提取到一个单独的 CSS 文件 (只用在生产环境中)
# 也可以是一个传递给 `extract-text-webpack-plugin` 的选项对象
extract: true,
# 是否开启 CSS source map?
sourceMap: false,
# 为预处理器的 loader 传递自定义选项。比如传递给 # Css-loader 时,使用 `{ Css: { ... } }`。
loaderOptions: {
css: {
# 这里的选项会传递给 css-loader
},
postcss: {
# 这里的选项会传递给 postcss-loader
}
},
# 为所有的 CSS 及其预处理文件开启 CSS Modules。 # 这个选项不会影响 `#.vue` 文件。
modules: false
},
# 在生产环境下为 Babel 和 TypeScript 使用 `thread-loader` # 在多核机器下会默认开启。
parallel: require('os').cpus().length > 1,
# PWA 插件的选项。 # 查阅 https:#github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli-plugin-pwa/README.md
pwa: {},
# 三方插件的选项
pluginOptions: {
# ...
}
}
.browserslistrc文件配置或者是package.json中配置的browserslist对象
具体配置参考链接:browserslist 目标浏览器配置表
> 1% #全球超过1%人使用的浏览器
last 2 versions#所有浏览器兼容到最后两个版本根据CanIUse.com追踪的版本
not dead
网友评论