本文是参考2020年了,再不会webpack敲得代码就不香了(近万字实战)一点一点手动敲代码实际的,不同的中央就是当初是webpack5+vue3。
一、初始化
1. 创立文件夹antdv,cd antdv
,执行npm init
初始化
2. 装置webpack
、webpack-cli
npm i -D webpack webpack-cli
这里有个留神点是在我的项目内装置而不是全局装置webpack
和webpack-cli
,因为有时候不同我的项目须要不同版本的webpack
。
这里装置了最新版本的:
3. 测试下webpack
的打包
a) 在我的项目内(antdv文件夹下)新建src/main.js
b) 在package.json配置命令
"scripts": {
"build": "webpack ./src/main.js"
},
c) 运行胜利,会呈现一个dist文件夹,里边是打包后的main.js
二、开始配置
1. 新建/build/webpack.config.js
// webpack.config.js
const path = require('path')
module.exports = {
mode: 'development', // 开发模式
entry: path.resolve(__dirname, '../src/main.js'), // 入口文件
output: {
filename: 'main.js', // 打包后的文件名称
path: path.resolve(__dirname, '../dist') // 打包后输入的文件所在的目录
}
}
其中dist文件夹中的main.js就是咱们须要在浏览器中理论运行的文件。咱们在html文件中引入打包好的js。然而为了每次改变浏览器都能拿到最新的js,咱们往往会这样配置:
// webpack.config.js
const path = require('path')
module.exports = {
mode: 'development', // 开发模式
entry: path.resolve(__dirname, '../src/main.js'), // 入口文件
output: {
filename: '[name].[hash:8].js', // 每次打包hash值会变,这样就不会被浏览器缓存命中,每次就能拿到最新的文件
path: path.resolve(__dirname, '../dist') // 打包后输入的文件所在的目录
}
}
2. 配置在html中主动引入打包好的js文件
a) 装置插件HtmlWebpackPlugin
npm i -D html-webpack-plugin
b) 新建/public/index.html,外面能够什么都不写
c) 配置插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html')
})
]
}
d) 运行npm run build
而后就会发现打包好的js曾经主动引入到index.html里去了
3. 清理dist文件夹残留的上次打包的文件
a) 装置clean-webpack-plugin
npm i -D html-webpack-plugin
b) 成果
4. 配置css loader、scss loader
a) 装置loader
npm i -D style-loader css-loader
cnpm i --save-dev node-sass // 这个如果用npm的话很慢,所以换了源
b) 配置loader
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'] // 从右向左解析
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
},
// ...
}
5. 为浏览器增加前缀
a) 装置loader
npm i --save-dev postcss-loader autoprefixer
b) 配置loader
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'] // 从右向左解析
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
}
]
c) 执行成果
【问题1】如果这么写?
rules:[{
test:/\.scss$/,
use:['style-loader','css-loader',{
loader:'postcss-loader',
options:{
plugins:[require('autoprefixer')]
}
},'sass-loader'] // 从右向左解析准则
}]
报错如下?
ValidationError: Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema.
- options has an unknown property 'plugins'. These properties are valid:
object { postcssOptions?, execute?, sourceMap? }
at Object.loader
【解决】则参考官网新写法传送门,以上写法是webpack 4.x版本的写法。当初我的项目装置的是webpack 5.x。
【问题2】这里如果执行npm run build的时候报错?
Module Error (from ./node_modules/sass-loader/dist/cjs.js):
Cannot find module 'sass'
【解决】重新安装一下sass-loader和node-sass就好了。
6. 外链css
下面的第五步,打包后的css是以style标签的模式插入到了html中。如果想要css以外链的模式插入的话,就要这样(会将所有的css款式合并为一个css文件):
- 装置
npm i --save-dev mini-css-extract-plugin
- 配置
// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', {
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'autoprefixer'
],
],
},
}
}] // 从右向左解析
},
{
test: /\.scss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', {
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'autoprefixer',
{
// Options
},
],
],
},
}
}, 'sass-loader']
}
]
},
plugins: [
// ...
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[id].css'
})
]
}
7.打包 图片、字体、媒体、等文件
- 装置插件
npm i --save-dev file-loader url-loader
- 配置
rules: [
// ...
{
test: /\.(jpe?g|png|gif)$/i, // 图片文件
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: 'fonts/[name].[hash:8].[ext]'
}
}
}
}
]
}
]
8.用babel本义js文件
a) babel-loader是将es6/7/8的代码转换成es5的。
- 装置插件
npm i --save-dev babel-loader @babel/preset-env @babel/core
- 配置
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
exclude: /node_modules/
}
b) 下面的babel-loader只会转换es6/7/8的代码成es5,而并不会转换新api,比方promise/set/map等等,上面装置babel-polyfill实现这项工作
- 装置插件
npm i @babel/polyfill
- 配置
entry: ['@babel/polyfill', path.resolve(__dirname, '../src/main.js')], // 入口文件
三、搭建vue3开发环境
vue-loader 用于解析.vue文件
vue-template-compiler 用于编译模板
0. npm i vue@next
1. npm i --save-dev vue-loader vue-template-compiler vue-style-loader
2. 简略新建app.vue/main.js/index.html
// src/app.vue
<template>
<div id="counter">
Counter: {{ counter }}
</div>
</template>
<script>
export default {
data() {
return {
counter: 0
}
}
}
</script>
<style lang="scss" scoped>
div {
color: #000;
}
</style>
// src/main.js
import { createApp } from 'vue'
import App from './app.vue'
createApp(App).mount('#app')
// public/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>0-1 webpack</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
3. 配置
+ const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
// ...
+ resolve: {
+ alias: {
+ 'vue$': 'vue/dist/vue.runtime.esm.js',
+ '@': path.resolve(__dirname, '../src')
+ },
+ extensions: ['*', '.js', '.json', '.vue']
+ },
module: {
rules: [
// ...
+ {
+ test: /\.vue$/,
+ use: ['vue-loader']
+ }
]
},
plugins: [
// ...
+ new VueLoaderPlugin()
]
}
4. 配置打包命令
// package.json
"scripts": {
+ "serve": "webpack-dev-server --config build/webpack.config.js --open"
},
4.1 此时运行npm run serve
,报错如下:
网上说如同是webpack的版本和webpack-dev-server版本不统一的问题。找到了这个问题传送门,而后试了下,把命令的webpack-dev-server
换成webpack serve
,再运行npm run serve
就没报那个错了。
// package.json
"scripts": {
- "serve": "webpack-dev-server --config build/webpack.config.js --open",
+ "serve": "webpack serve --config build/webpack.config.js"
},
4.2 解决了上述问题之后再持续运行npm run serve
,后果又报错:
说是vue-loader跟vue-template-compiler的版本不统一
去找了下vue3的配置,vue-template-compiler换成了@vue/compiler-sfc。
// package.json
- "vue-template-compiler": "^2.6.12",
+ "@vue/compiler-sfc": "^3.0.2",
4.3 而后又有这个问题
我就去github照着and-design-vue的库的webpack配置比照了一下,降级了vue-loader的版本:
// webpack.config.js
alias: {
- vue$: 'vue/dist/vue.runtime.esm.js',
+ vue$: 'vue/dist/vue.esm-bundler.js',
'@': path.resolve(__dirname, '../src')
},
// package.json
- "vue-loader": "^15.9.5",
+ "vue-loader": "^16.0.0-rc.2",
4.4 因为降级了vue-loader的版本,又会呈现这个问题:
包扭转了,所以去找了一下vue-loader的包,长这个样子
plugin.js里边长这个样子
所以原来的配置也须要批改一下
// webpack.config.js
- const VueLoaderPlugin = require('vue-loader/dist/plugin')
+ const VueLoaderPlugin = require('vue-loader/dist/plugin').default
4.5 最初,终于解决了
运行npm run serve
,不再报错!!!
至此,vue3的配置实现。
5. 辨别生产环境与开发环境
// webpack.config.js
const path = require('path')
- const Webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const VueLoaderPlugin = require('vue-loader/dist/plugin').default
+ const devMode = process.argv.indexOf('--mode=production') === -1
module.exports = {
- mode: 'development', // 开发模式
entry: ['@babel/polyfill', path.resolve(__dirname, '../src/main.js')], // 入口文件
output: {
filename: '[name].[hash:8].js', // 打包后的文件名称
path: path.resolve(__dirname, '../dist'), // 打包后输入的文件所在的目录
+ chunkFilename: 'js/[name]:[hash:8].js'
},
resolve: {
alias: {
vue$: 'vue/dist/vue.esm-bundler.js',
'@': path.resolve(__dirname, '../src')
},
extensions: ['*', '.js', '.json', '.vue']
},
- devServer: {
- port: 3000,
- hot: true,
- contentBase: '../dist'
- },
module: {
rules: [
{
test: /\.css$/,
use: [
+ {
+ loader: devMode ? 'vue-style-loader': MiniCssExtractPlugin.loader,
+ options: {
+ publicPath: '../dist/css/',
+ hmr: devMode
+ }
+ },
- MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'autoprefixer'
]
]
}
}
}] // 从右向左解析
},
{
test: /\.scss$/,
use: [
+ {
+ loader: devMode ? 'vue-style-loader': MiniCssExtractPlugin.loader,
+ options: {
+ publicPath: '../dist/css/',
+ hmr: devMode
+ }
+ },
- MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'autoprefixer'
]
]
}
}
}, 'sass-loader']
},
{
test: /\.(jpe?g|png|gif)$/i, // 图片文件
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: 'fonts/[name].[hash:8].[ext]'
}
}
}
}
]
},
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
exclude: /node_modules/
},
{
test: /\.vue$/,
use: [
- 'vue-loader'
+ {
+ loader: 'vue-loader',
+ options: {
+ compilerOptions: {
+ preserveWhitespace: false
+ }
+ }
+ }
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html')
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
- filename: '[name].[hash].css',
- chunkFilename: '[id].[hash].css'
+ filename: devMode ? '[name].css' : '[name].[hash].css',
+ chunkFilename: devMode ? '[id].css' : '[id].[hash].css'
}),
new VueLoaderPlugin(),
- new Webpack.HotModuleReplacementPlugin()
]
}
// 开发环境配置文件
// 开发环境次要实现的是热更新,不要压缩代码,残缺的sourceMap
const Webpack = require('webpack')
const webpackConfig = require('./webpack.config.js')
const { merge } = require('webpack-merge')
module.exports = merge(webpackConfig, {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
devServer: {
port: 3000,
hot: true,
contentBase: '../dist'
},
plugins: [
new Webpack.HotModuleReplacementPlugin()
]
})
最初,生产环境配置如下:
// webpack.prod.js
// 生产环境配置文件
// 生产环境次要实现的是压缩代码、提取css文件、正当的sourceMap、宰割代码
const path = require('path')
const webpackConfig = require('./webpack.config.js')
const { merge } = require('webpack-merge')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
module.exports = merge(webpackConfig, {
mode: 'production',
devtool: 'nosources-source-map',
plugins: [
new CopyWebpackPlugin({
patterns:
[{
from: path.resolve(__dirname, '../public'),
to: path.resolve(__dirname, '../dist')
}]
})
],
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
terserOptions: {
warnings: false,
compress: {
drop_console: true,// 正文console
drop_debugger: true, // 正文debugger
pure_funcs: ["console.log"]
}
}
}),
new OptimizeCssAssetsPlugin({})
],
splitChunks: {
chunks: "async",
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
})
【留神】如果遇到以下问题
(1)遇到WebpackMerge is not a function
应该是webpack-merge这个插件版本差别的起因,网上搜了以下,应该是插件的导出不一样了,所以?:
// webpack.dev.js && webpack.prod.js
- const WebpackMerge = require('webpack-merge')
- module.exports = WebpackMerge(webpackConfig, {
+ const { merge } = require('webpack-merge')
+ module.exports = merge(webpackConfig, {
(2)devtool should match pattern
大略是webpack5的devtool配置选项的值是有固定的几个选项的。而后去官网看了一下,是有我写的选项的,然而却报了下面这个错,而后我就依据谬误的提醒去批改了一下?:
// webpack.dev.js
- devtool: 'cheap-module-eval-source-map',
+ devtool: 'eval-cheap-module-source-map',
(3)如果装了uglifyjs
插件来压缩js
的,请把它卸载掉。当初城里人都用terser plugin
了orz
装了uglify的时候,执行npm run build
打包始终报错:
而后在网上找都说是不辨认es6的语法等等,我看我也配置好了呀:
// webpack.config.js
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
exclude: /node_modules/
},
// ...
]
甚至还加了个.babelrc文件。。。还是一样报错。
认真比照官网中文文档,配置也一毛一样orz
起初搜寻的时候看到有个搜寻选项写着uglify废除
字样的,我就想该不会这个插件真的是曾经废除了吧,所以才始终没起作用报错,而后我就转去英文文档,看了一下,果然曾经被terser
取代了/(ㄒoㄒ)/~~
(4)`WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
main.a33e7a8f.js (440 KiB)`
这个是说入口文件大小超过设置了,那么这里就改一下设置(或者改成performance: false
):
// webpack.prod.js
module.exports = merge(webpackConfig, {
// ...
+ performance: {
+ maxEntrypointSize: 500000,
+ maxAssetSize: 500000
+ }
}
优化webpack配置
优化打包速度
放大文件的搜寻范畴(配置include exclude alias noParse extensions)
其实这个后面就有配置过,只是没有阐明
alias
– 文件门路别名,个别就是简化文件门路include
– 给loader指明路线,该去哪里(目录)解析文件,而不用理睬别的中央的文件exclude
– 通知loader,匹配到这个的门路下的文件不用解析noParse
– 不用解析匹配上的目录里的文件extensions
– 文件扩展名,依据扩展名查找文件,频率高的优先写在后面
// webpack.config.js
resolve: {
alias: {
vue$: 'vue/dist/vue.esm-bundler.js',
'@': path.resolve(__dirname, '../src'),
+ 'assets': path.resolve('src/assets'),
+ 'components': path.resolve('src/components')
},
+ extensions: ['*', '.js', '.json', '.vue']
},
// ...
rules: [
{
test: /\.js$/,
use: [{
loader: 'happypack/loader?id=happyBabel',
}],
+ include: [path.resolve(__dirname, '../src')],
+ exclude: /node_modules/
},
{
test: /\.vue$/,
use: [{
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
}
}
}],
+ include: [path.resolve(__dirname, '../src')],
+ exclude: /node_modules/
},
// ...
]
这里要留神的点就是,文件的门路!因为我的webpack配置文件都是在根目录下build文件夹里(`/build/webpack.*.js`),所以这里`path.resolve(__dirname, '../src')`是`../`
如果门路不对,比如说我上边要是写成src
,就报了如下谬误:
应用HappyPack开启多过程Loader转换
-
装置
npm i --save-dev happypack
-
配置
// webpack.config.js + const HappyPack = require('happypack') + const os = require('os') + const webpack = require('webpack') + const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }) // ... rules: [ { test: /\.js$/, use: [{ loader: 'happypack/loader?id=happyBabel', - options: { - presets: ['@babel/preset-env'] - } }], include: [path.resolve(__dirname, '../src')], exclude: /node_modules/ }, // ... ] // ... plugins: [ // ... + new HappyPack({ + id: 'happyBabel', // 与loader对应的id标识 + loaders: [ // 用法和loader的配置一样,留神这里是loaders + { + loader: 'babel-loader', + options: { + presets: [ + ['@babel/preset-env'] + ], + cacheDirectory: true + } + } + ], + threadPool: happyThreadPool // 共享过程池 + }), ]
应用webpack-parallel-uglify-plugin 加强代码压缩
退出进去,配置之后运行npm run build报错了,如同是因为它是依赖uglifyJS的,而之前下面提到过,uglifyJS在webpack5曾经被废除了,而后这个临时不晓得如何革新。。。
抽离第三方模块
我的项目中依赖的库,比如说vue、elementui、axios等,因为它们很少会变更,所以咱们不心愿每一次构建都要继承这些依赖,这样会拖慢构建速度。所以如果把这些第三方库抽取进去,每次构建只打包咱们我的项目中的文件,就能够进步构建速度。当降级第三方库的时候才从新打包这些库。
- 装置
应用webpack
内置的插件:DllPlugin
、DllReferencePlugin
-
新建
webpack.vendor.config.js
// webpack.vendor.config.js const path = require('path') const webpack = require('webpack') module.exports = { // 要打包的第三方模块的数组 entry: { vendor: ['vue'] }, output: { path: path.resolve(__dirname, '../static/js'), // 打包后文件输入的地位 filename: '[name].dll.js', library: '[name]_[fullhash]' // 这里须要和webpack.DllPlugin中的`name: '[name]_[fullhash]'`保持一致 }, plugins: [ new webpack.DllPlugin({ path: path.join(__dirname, '../[name]-manifest.json'), context: __dirname, name: '[name]_[fullhash]' }) ] }
-
更改
webpack.config.js
+ const CopyWebpackPlugin = require('copy-webpack-plugin') // ... plugins: [ // ... + new webpack.DllReferencePlugin({ + context: __dirname, + manifest: require('../vendor-manifest.json') + }), + new CopyWebpackPlugin({ // 拷贝生成的文件到dist目录 这样每次不用手动去cv + patterns: + [{ + from: 'static', + to: 'static' + }] + }) ]
-
package.json
增加命令"dll": "webpack --config build/webpack.vendor.config.js"
- 执行
npm run dll
就会生成文件 -
在html文件中手动引入打包好的依赖文件
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>0-1 webpack</title> + <script src="../static/js/vendor.dll.js"></script> </head> <body> <div id="app"></div> </body> </html>
-
留神
这里要留神的点仍然是文件门路????。比方这里的static文件夹,是要生成要根目录下的,而我的配置文件是在build文件夹里的,所以这里要留神下它们之间的相对路径。
- 总结
援用2020年了,再不会webpack敲得代码就不香了(近万字实战):
这样如果咱们没有更新第三方依赖包,就不用npm run dll。间接执行npm run dev npm run build的时候会发现咱们的打包速度显著有所晋升。因为咱们曾经通过dllPlugin将第三方依赖包抽离进去了。
发表回复