⚠️ 现在 (2021年) Webpack 已经升级至 V5,而文章的大部分内容是基于 V4 的。
错误信息
ERROR in ./src/component/App.vue
Module Error (from ./node_modules/vue-loader/lib/index.js):
vue-loader was used without the corresponding plugin.
Make sure to include VueLoaderPlugin in your webpack config.
// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
// ...
plugins: [
new VueLoaderPlugin() // 新增VueLoaderPlugin
]
// ...
}
错误信息
ERROR in ./src/component/App.vue
Module build failed (from ./node_modules/vue-loader/lib/index.js):
Error: [vue-loader] vue-template-compiler must be installed as a peer dependency,
or a compatible compiler implementation must be passed via options.
由于 vue-loader
内部实现需要依赖 vue-template-compiler
,但我们通过 npm 安装 vue-loader
时并未同时安装 vue-template-complier
,所以需要显示的在本地项目安装一下:
npm i vue-template-complier --save-dev
错误信息
ERROR in ./src/component/App.vue?vue&type=style&index=0&lang=css&
(./node_modules/vue-loader/lib??vue-loader-options!./src/component/App.vue?vue&type=style&index=0&lang=css&) 24:0
Module parse failed: Unexpected token (24:0)
You may need an appropriate loader to handle this file type.
|
|
> .app {
| font-size: 24px;
| margin-top: 50%;
由于默认的 .vue
文件中 <style>
的lang属性值为 css
。
所以 vue-loader
默认会将 <style>
块中的样式文件抽出作为 CSS 文件提供给 Webpack 加载。
而不配置 css-loader
的话 Webpack 是无法正确加载 CSS 文件的:
npm i css-loader --save-dev
修改配置文件:
// webpack.config.js
module.exports = {
// ...
module: {
rules: [{
test: /.css$/,
use: 'css-loader'
}]
}
// ...
}
凭以上配置虽然页面能正常访问,但会发现样式并不会生效
npm i style-loader --save-dev
修改配置文件:
// webpack.config.js
module.exports = {
// ...
module: {
rules: [{
test: /.css$/,
use: [
'style-loader',
'css-loader'
]
}]
}
// ...
}
CSS Loader 的作用是使我们可以在 JS 文件中通过 import style from 'xxx.css'
来告诉 Webpack 这个文件对样式的依赖,这样当打包时这个 CSS 文件时也会将 xxx.css 算入其中,当这样并不能使样式文件被浏览器正确加载。
Style Loader 正是起到将依赖的样式文件通过在页面上插入 <style>
标签的形式注入到页面中,所以简单来讲,不做其他特殊配置的话,一定要同时使用这俩个 Loader 才能使得样式正确加载。
错误信息
// 页面报错
[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available.
Either pre-compile the templates into render functions, or use the compiler-included build.
页面并未正常渲染,而是在 HTML
中出现了如下片段
<html>
<head>
<title>Vue</title>
</head>
<body>
<!--function (a, b, c, d) { return createElement(vm, a, b, c, d, true); }-->
<script type="text/javascript" src="main.js"></script>
</body>
</html>
这个问题出现主要是由于 Vue 对外提供了俩个版本的最终构造文件 Full(Runtime + Compiler) 以及 Runtime-only,俩者的区别就在于一个有 Compiler 另一个没有。
此外,在 Vue 的 package.json 中可以看到 "module": "dist/vue.runtime.esm.js"
也就是说在使用 webpack 打包时 import Vue from 'vue'
默认使用的是 vue.runtime.esm.js 也就是 Rutime-only 的版本。
Compiler 的主要作用在于将文件中的模版字符串转化为 render 函数(注意这里的文件不包括 .vue 文件 因为他们会被 vue-loader 自动预编译)
举例,在如下 Vue 应用的主入口文件中:
// main.js
import Vue from 'vue'
// 这种形式需要引用 Full(Runtime + Compiler)
new Vue({
el: '#app',
template: '<div></div>'
})
// 引用 Runtime-only 即可
new Vue({
el: '#app',
rendre(h) {
return h('div')
}
})
所以以上问题的解决方案有俩种,其一是如果我们确定不会在除 .vue 文件外使用 Vue 的 template,那么直接照常引入默认的 Runtime-only 版本即可,否则则需要我们显示的引入 Full(Runtime + Compiler) 版本。
// main.js
import Vue from 'vue/dist/vue.esm.js'
可以利用 webpack 的别名功能简化这个操作,也是官方推荐的方式
// webpack.config.js
module.exports = {
// ...
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
// Ps: 最后的 $ 意味着完全匹配 也就是说只有在 import xx from 'vue' 时这条规则才会生效
// import xx from 'vue/xx.js' 并不会触发以上规则
}
}
// ...
}
之后只要照常引用 import Vue from 'vue'
则会正确引入 Full(Runtime + Compiler) 版本
Ps: 基于 Babel V7
npm install
babel-plugin-syntax-jsx
babel-plugin-transform-vue-jsx
babel-helper-vue-jsx-merge-props
babel-preset-env
--save-dev
// babel.config.js
module.exports = function (api) {
api.cache(true)
return {
presets: ['env'],
plugins: ['transform-vue-jsx']
}
}
// webpack.config.js
module.exports = {
// ...
module: {
rules: [{
test: /.js$/,
use: 'babel-loader'
}]
}
// ...
}
错误信息
// 如果在 devServer 中开启了 hot 确并未配置 HotModuleReplacementPlugin
// 浏览器控制台会出现如下报错
Uncaught Error: [HMR] Hot Module Replacement is disabled.
at eval (webpack:///(:9000/webpack)/hot/dev-server.js?:7:8)
at Object../node_modules/webpack/hot/dev-server.js (main.js:485)
at __webpack_require__ (main.js:20)
at eval (webpack:///multi_(:9000/webpack)-dev-server/client?:2:1)
at Object.0 (main.js:578)
at __webpack_require__ (main.js:20)
at main.js:84
at main.js:87
// webpack.dev.config.js
const webpack = require('webpack')
module.exports = {
// ...
devServer: {
// ...
hot: true
},
plugins: [
// ...
new webpack.HotModuleReplacementPlugin()
]
// ...
}
url(xxx)
引入的的图片路径不正确当我们通过配置使得输出的静态文件放到不同目录下时很容易碰到这个问题,例如我们将打包后的文件分别放到js/css/images三个目录下。
这时如果没有特意配置过的话被 url-loader
处理过后的图片引用路径通常会变为 images/xxx.png
。如果是在根目录下的 HTML 中的文件下这样访问图片不会有问题,但如果是在 CSS 中的 url(images/xxx.png)
则会出现找不到图片的问题。
因为这种形式的 URL 是一种相对路径,而 CSS 文件时放在 css 目录下的,而这个路径下并没有 images 文件夹,所以当然找不到正确的图片。
这时可通过配置 url-loader
的 publicPath
为相对服务器根目录的相对路径来解决。
关于 publicPath
有如下这些配置方式
module.exports = {
//...
output: {
// One of the below
publicPath: 'https://cdn.example.com/assets/', // CDN (always HTTPS)
publicPath: '//cdn.example.com/assets/', // CDN (same protocol)
publicPath: '/assets/', // server-relative
publicPath: 'assets/', // relative to HTML page
publicPath: '../assets/', // relative to HTML page
publicPath: '', // relative to HTML page (same directory)
}
}
如果想要确保 Webpack 优先加载 Polyfill 代码,需要进行如下配置:
app: ['babel-polyfill', './app.js']
详情可参见 React
的Issue
optimization.moduleIds
Webpack V4 新引入的这条配置其功能相当于之前的 xxxModuleIdsPlugin
,一共有 natural/named/hashed/size/total-size
这五个可选值,默认为 false
即以一个自增的数字作为 moduleId。
首先要理解在 Webpack 中 module 的含义就是我们在代码里每一处 import xx from 'xx'
中的 xx 模块,moduleId 即是 Webpack 在打包过程中赋予每一个模块的唯一 Id。
个人认为在开发环境下将其设为 named
也就是每个模块的文件路径作为 Id 可以方便 Debug,在生产环境下将其设为 hashed
避免每次改动都导致所有模块的 Id 发生变化。
Webpack 提供了 Tree Shaking 的功能,帮助我们在打包的过程把无用的代码块移除,进一步减小包的体积。在V4中要正确启用这个功能需要确保下列这几点:
import
以及 export
来管理 Modulemode
设为 production
package.json
中加入 "sideEffects": false
或者 "sideEffects": ["*.css"]
避免不小心移除样式文件@babel/preset-env
注意在 .babelrc 中将其默认的配置 "modules": "commonjs"
改为 "modules": false
也就是说不让 Babel 转义源码中的 ES6 Module 语法moment
的国际化文件很大,所以在生产环境打包时要留意不要将不必要的国际化文件也包含进来,可以通过如下 Webpack 插件来解决这个问题:
plugins: [
// 以下的配置会使打包出来的文件只包含简体以及繁体中文的国际化
new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn|zh-tw/)
]
Webpack中的 url-loader
和 file-loader
都是用于打包一些图片字体之类的静态资源文件,区别在于 URL Loader 会对一定大小限制内的图片进行 Base64 编码并采用 DataUrl 的形式嵌入页面或样式表。
优点在于经过 URL Loader 编码后的图片不会占用 HTTP 请求。但在图片过大的情况下会增加文件的大小,得不偿失,更适用于处理一些项目中多处用到的小图片(1kb以下)。
eslint 的 import/no-unresolved
规则并不会识别 alias。
这时需要借助eslint-import-resolver-webpack,并在 .eslintrc
文件中增加配置项:
{
settings: {
'import/resolver': {
webpack: {
// 配置alias的文件路径
config: './webpack.base.js'
}
}
}
}
在使用 devServer
遇到问题时可以路由至URL /webpack-dev-server
。
通过观察打包出来的 bundle 文件详情来 Debug。