We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在前一篇文章中介绍了一些webpack配置常用的loader和插件,并且完成了一个适合大多数场景的基础配置文件;本文将继续介绍webpack的配置,相对于上一篇文章,本文更加着重开发效率和个性化需求的配置
前一篇文章中配置都只适用于单页应用的打包,但是在实际的工作也还会涉及到多页应用的项目开发;
webpack中多页应用与单页应用打包的不同之处主要体现在以下几点:
配置
<!-- webpack.config.js --> const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: { main: './main.js', miniApp: './miniApp.js' }, output: { filename: [name].[hsah].js, path: path.resolve(__dirname, 'dist') }, plugin: [ new HtmlWebpackPlugin({ template: './index.html', filename: 'main.html', chunks: ['main'] }), new HtmlWebpackPlugin({ template: './index.html', filename: 'miniApp.html', chunks: ['miniApp'] }) ] // 省略其他代码 }
[name]: 构建包的名称,它来源于entry中的key值,在单页应用中默认为main chunks: 用于指定需要引入到html中的js文件;与它相反,还有一个excludeChunks参数,它用于指定不需要引入的js文件
[name]
key
chunks
excludeChunks
日常的开发中会遇到一些直接使用的文件,这些文件可能是js,也可能是css或者是图片,它们不需要经过打包,可以直接将他们拷贝到webpack构建目录;但是手动拷贝不仅麻烦并且容易出错(比如,修改之后忘记拷贝)
copy-webpack-plugin插件用于将指定文件/文件夹复制到构建目录;通过这个插件可以将静态文件直接复制到输出目录中
copy-webpack-plugin
安装
npm install copy-webpack-plugin -D
<!-- webpack.config.js --> const path = require('path') const copyWebpackPlugin = require('copy-webpack-plugin') module.exports = { plugin: [ new copyWebpackPlugin([ { from: path.resolve(__dirname, './public'), to: './dist' } ]) ] }
from: 指定需要复制的文件夹 to: 指定复制后的文件夹
from
to
更多copyWebpackPlugin配置,可以参考官方文档
copyWebpackPlugin
在开发的过程中经常会通过控制台查看错误信息;但是打包之后的错误信息位置将会以构建后的js为基准,这样的错误信息因为没有定位到源码的错误位置,对调试很不友好
这个问题可以通过对devTool配置sourceMap来解决;sourceMap是一个源码映射文件,有多种格式可选,这里只列举几个有代表性的,更多sourceMap格式可以查看官方文档
devTool
sourceMap
source-map
eval-source-map
cheap-module-source-map
cheap-module-eval-source-map
综合实际使用情况和构建速度考虑,开发环境中一般使用cheap-module-eval-source-map
<!-- webpack.config.js --> module.exports = { // 省略其他代码 devtool: 'cheap-module-eval-source-map' }
跨域是前后端接口交互时一个很常见的问题,解决跨域发方式也有很多,这里主要介绍如何通过webpack的配置来解决跨域问题;
先通过server.js在本地创建一个node服务, 启动本地的3000端口作为后端服务
server.js
<!-- server.js --> const express = require('express') const app = express() app.get('/api/user', (req, res) => { res.json({name: '阿白Smile'}) }) app.listen(3000)
使用命令行工具执行node server.js命令启动node服务,然后使用client.js向后端服务发送请求
node server.js
client.js
<!-- client.js --> const xhr = new XMLHtttpRequest() xhr.open('GET', '/api/user', true) xhr.onload = function() { console.log(xhr.response) } xhr.send()
接下来就通过webpack为前端服务配置一个代理,将前端的服务代理到后端的服务上,这样可以让前端和服务端在同一个服务下,以此来解决跨域问题
<!-- webpack.config.js --> module.exports = { devServer: { prot: 3000, host: 'localhost' proxy: { '/api': 'http://localhost:3000' } } }
通过以上的配置,当client.js访问/api/user时,请求会被代理到http://localhost:3000/api/user
/api/user
http://localhost:3000/api/user
如果不想每次都在接口路径前加/api,或者后端的接口没有/api这一层路径,那么可以通过pathRewrite重写路径,将/api重写为空
/api
pathRewrite
<!-- webpack.config.js --> module.exports = { devServer: { prot: 3000, host: 'localhost' proxy: { '/api': { target: 'http://localhost:3000', pathRewrite: {'/api': ''} } } } }
通过服务端启动webpack,可以将前端和服务端启动在同一个服务上;当前后端都在同一个服务的时候自然就不存在跨域问题了
服务端启动webpack需要使用到webpack-dev-middleware中间件
webpack-dev-middleware
npm install webpack-dev-middleware -D
<!-- server.js --> const express = require('express') const webpack = require('webpack') const middle = require('webpack-dev-middleware') const app = express() let config = require('./webpack.config.js') let compiler = webpack(config) app.use(middle(compiler)) app.get('/user', (req, res) => { res.json({name: '阿白Smile'}) }) app.listen(3000)
使用这种方式的前提是能够操作服务器,不过既然都可以操作服务器了,那么还可以通过设置header来实现跨域
header
webpack-dev-server提供了一个before钩子;它的第一个参数暴露了webpack-dev-server内部的express服务,通过这个服务,可以完成一些数据的mock
webpack-dev-server
before钩子
express
<!-- webpack.config.js --> module.exports = { devServer: { before(app) { app.get('/name', (req, res) => { res.json('阿白Smile') }) } } }
mocker-api是一个为 REST API 创建mock的webpack-dev-server中间件。在后端服务还没有完成的时候,可以通过这个中间件进行mock数据
mocker-api
npm install mocker-api -D
创建一个mock.js进行接口和数据mock
mock.js
<!-- mock.js --> module.exports = { 'GET /userInfo/:id': (req, res) => { const { id } = req.params; // 省略查询 return res.json({ id, name: '阿白smile' }); } }
配置到devServer的before钩子中
devServer
<!-- webpack.config.js --> const path = require('path') const apiMocker = require('mocker-api'); const mockApi = path.resolve('./mock.js') module.exports = { devServer: { before(app) { mockApi(app, mockApi) } } }
修改client.js文件,访问/userInfo接口
/userInfo
<!-- client.js --> const xhr = new XMLHtttpRequest() xhr.open('GET', '/userInfo/1', true) xhr.onload = function() { console.log(xhr.response) } xhr.send()
使用npm run dev命令执行脚本之后,在浏览器中打开,此时浏览器会打印出{"id":"1","name":"阿白smile"}
npm run dev
{"id":"1","name":"阿白smile"}
webpack启动后会从入口文件开始找出所有依赖的模块;resolve的作用就是告诉webpack如何寻找这些模块所对应的文件
resolve
resolve.alias用于配置别名,通过别名可以把原来的导入路径映射到一个新的路径, 它可以让模块的引入更加简单;vue中常常为src文件夹设置一个别名为@
resolve.alias
src
@
文件结构
|--src |--|--assets |--|--|--main.css
<!-- webpack.config.js --> module.exports = { // 省略其他代码 resolve: { alias: { '@': './src' } } }
使用
import '@/assets/main.css'
使用别名之后,别名会直接映射到别名所指定的路径;在使用相关模块时,可以使用更加简单的方式书写模块路径
resolve.modules用于指定webpack解析模块时从哪些目录下搜索模块,默认情况下,webpack只从node_modules中搜索;
resolve.modules
node_modules
如果有一个模块是自己编写的文件,那么它就不需要到node_modules中取查找;比如, 我们经常会在src/components中编写一些组件,为了避免每次引用组件都写很长的路径,就可以通过modules配置来简化以下路径
src/components
modules
|--src |--|--components |--|--|--navMenu.vue
<!-- webpack.config.js --> module.exports = { // 省略其他代码 resolve: { modules: ['./src/components', 'node_modules'] } }
import navMenu from 'navMenu'
webpack在搜索模块时,会根据modules的配置从左到右开始搜索;所以,在使用navMenu时,会先从./src/components中查找,如果没有找到,则会去node_modules中查找;
navMenu
./src/components
在模块导入语句中没有带文件后缀时,webpack会自动带上尝试后缀去访问(默认带.js); resolve.extensions用于配置尝试访问的后缀列表,列表的优先级为从左到右
.js
resolve.extensions
<!-- webpack.config.js --> module.exports = { // 省略其他代码 resolve: { extensions: ['.js', '.vue', '.json'] } }
import index from './index'
上面的代码结合配置之后,会先尝试使用.js作为后缀去访问index,如果不存在则尝试使用.vue作为后缀去访问;以此类推,如果配置的后缀都尝试了还没有找到文件,则会报错,文件未找到
.vue
在实际开发中,开发环境和线上生产环境往往会使用不同的服务和域名,并且不同的环境应该是可以自动切换的,这就需要在代码中使用环境变量来区分不同的环境,然后自动切换相应的服务和域名
webpack通过内置的DefinePlugin插件可以提供了一个区分环境的全局变量
DefinePlugin
DefinePlugin的键值都是一个标志符或者多个用 . 连接起来的标志符
.
如果value是一个字符串,它会被当作一个代码片段来使用
value
如果value不是字符串,它会被转化为字符串(包括函数)
如果value是一个对象,它所有的 key 会被同样的方式定义(即全局可以直接访问对象的key,value则会应用DefinePlugin的键值规则)
如果在一个 key 前面加了 typeof,它会被定义为 typeof 调用
typeof
module.exports = { plugin: { new webpack.DefinePlugin({ DEV: JSON.stringify('dev') }) } }
if (DEV) { // 开发 BASE_URL: '' // 开发服务 } else { // 生产 BASE_URL: '' // 生产服务 }
通过环境变量可以进行环境的区分,但是代码中如果涉及到多处需要区分且每次都需要手动去配置,这不仅加大了工作量而且还容易出错;
一般的解决方案是将不同环境的配置分开到不同的文件,然后使用webpack-merge将其与基础配置进行合并
webpack-merge
webpack.base.config.js: 基础配置文件 webpack.pro.config.js: 线上生产环境配置文件 webpack.dev.config.js: 开发环境配置文件
webpack.base.config.js
webpack.pro.config.js
webpack.dev.config.js
webpack-merge是一个函数,它提供了合并功能,接收一个或多个对象/数组,用于对象的合并与数组的连接,返回合并后的对象;在合并对象时,如果同一个key出现多次,则后面的覆盖前面的
npm install webpack-merge
const merge = require('webpack-merge') let result = merge({name: '阿白smile', age: 18}, {age: 24, location: '北京'}) // 合并后的结果 // {name: '阿白smile', age: 24, location: '北京'}
webpack.base.config.js是基础的webpack配置,各个环境可以通用,所以webpack.base.config.js需要作为merge的第一个参数
开发环境的配置
<!-- webpack.dev.config.js --> const baseWebpackConfig = require('./webpack.base.config') const merge = require('webpack-merge') let devWebpackConfig = merge(baseWebpackConfig, { mode: 'development', devServer: { // 省略其他代码 } }) moudle.export = devWebpackConfig
生产环境的配置
<!-- webpack.pro.config.js --> const baseWebpackConfig = require('./webpack.base.config') const merge = require('webpack-merge') let proWebpackConfig = merge(baseWebpackConfig, { mode: 'production', // 省略其他代码 }) moudle.export = proWebpackConfig
配置命令脚本
<!-- package.json --> "scripts": { "dev": "webpack-dev-server --config=webpack.dev.config.js" "build": "webpack --config=webpack.pro.config.js" }
热更新主要应用在开发环境,当代码更改之后页面上只更新被修改的部分,不需要刷新页面,对开发和调试非常有利;
webpack中的热更新的配置主要依赖devServer.hot以及webpack的内置插件HotModuleReplacementPlugin
devServer.hot
HotModuleReplacementPlugin
<!-- webpack.config.js --> module.exports = { devServer: { hot: true // 启用热更新 }, plugin: [ new webpack.HotModuleReplacementPlugin() // 热更新插件 ] }
以上的代码配置之后在浏览器查看,会发现还是会刷新整个页面;这个时候还需要在入口文件中做以下配置
if (module.hot) { module.hot.accept() }
module.hot用于通知webpack此模块可以用于热更新,更多信息可以参考官方文档
module.hot
通过本篇文章中介绍的webpack配置,可以满足更加个性化的开发需求以及更高效率的开发
本文所对应的配置源码已提交到我的github
END
The text was updated successfully, but these errors were encountered:
No branches or pull requests
在前一篇文章中介绍了一些webpack配置常用的loader和插件,并且完成了一个适合大多数场景的基础配置文件;本文将继续介绍webpack的配置,相对于上一篇文章,本文更加着重开发效率和个性化需求的配置
多页应用打包
前一篇文章中配置都只适用于单页应用的打包,但是在实际的工作也还会涉及到多页应用的项目开发;
webpack中多页应用与单页应用打包的不同之处主要体现在以下几点:
配置
[name]
: 构建包的名称,它来源于entry中的key
值,在单页应用中默认为mainchunks
: 用于指定需要引入到html中的js文件;与它相反,还有一个excludeChunks
参数,它用于指定不需要引入的js文件静态数据拷贝
日常的开发中会遇到一些直接使用的文件,这些文件可能是js,也可能是css或者是图片,它们不需要经过打包,可以直接将他们拷贝到webpack构建目录;但是手动拷贝不仅麻烦并且容易出错(比如,修改之后忘记拷贝)
copy-webpack-plugin
插件用于将指定文件/文件夹复制到构建目录;通过这个插件可以将静态文件直接复制到输出目录中安装
配置
from
: 指定需要复制的文件夹to
: 指定复制后的文件夹更多
copyWebpackPlugin
配置,可以参考官方文档devTool(配置sourceMap)
在开发的过程中经常会通过控制台查看错误信息;但是打包之后的错误信息位置将会以构建后的js为基准,这样的错误信息因为没有定位到源码的错误位置,对调试很不友好
这个问题可以通过对
devTool
配置sourceMap
来解决;sourceMap是一个源码映射文件,有多种格式可选,这里只列举几个有代表性的,更多sourceMap格式可以查看官方文档source-map
: 原始源代码,会单独生成源码文件,可以提示错误信息的列和行eval-source-map
: 原始源代码,不会产生单独的文件,但是可以显示行和列cheap-module-source-map
: 转换后的代码,生成单独的文件,可以提示行但不能提示列cheap-module-eval-source-map
: 原始源代码,集成在打包后的文件中,不会生成独立的文件,可以提示行,但不能提示列综合实际使用情况和构建速度考虑,开发环境中一般使用
cheap-module-eval-source-map
webpack解决跨域问题
跨域是前后端接口交互时一个很常见的问题,解决跨域发方式也有很多,这里主要介绍如何通过webpack的配置来解决跨域问题;
准备工作
先通过
server.js
在本地创建一个node服务, 启动本地的3000端口作为后端服务使用命令行工具执行
node server.js
命令启动node服务,然后使用client.js
向后端服务发送请求普通代理配置
接下来就通过webpack为前端服务配置一个代理,将前端的服务代理到后端的服务上,这样可以让前端和服务端在同一个服务下,以此来解决跨域问题
通过以上的配置,当
client.js
访问/api/user
时,请求会被代理到http://localhost:3000/api/user
如果不想每次都在接口路径前加
/api
,或者后端的接口没有/api
这一层路径,那么可以通过pathRewrite
重写路径,将/api
重写为空通过服务端启动webpack
通过服务端启动webpack,可以将前端和服务端启动在同一个服务上;当前后端都在同一个服务的时候自然就不存在跨域问题了
服务端启动webpack需要使用到
webpack-dev-middleware
中间件安装
配置
使用这种方式的前提是能够操作服务器,不过既然都可以操作服务器了,那么还可以通过设置
header
来实现跨域mock数据
webpack-dev-server
提供了一个before钩子
;它的第一个参数暴露了webpack-dev-server
内部的express
服务,通过这个服务,可以完成一些数据的mock普通数据mock
使用mockerAPI
mocker-api
是一个为 REST API 创建mock的webpack-dev-server
中间件。在后端服务还没有完成的时候,可以通过这个中间件进行mock数据安装
配置
创建一个
mock.js
进行接口和数据mock配置到
devServer
的before钩子
中修改
client.js
文件,访问/userInfo
接口使用
npm run dev
命令执行脚本之后,在浏览器中打开,此时浏览器会打印出{"id":"1","name":"阿白smile"}
配置resolve属性
webpack启动后会从入口文件开始找出所有依赖的模块;
resolve
的作用就是告诉webpack如何寻找这些模块所对应的文件alias (配置别名)
resolve.alias
用于配置别名,通过别名可以把原来的导入路径映射到一个新的路径, 它可以让模块的引入更加简单;vue中常常为src
文件夹设置一个别名为@
文件结构
配置
使用
使用别名之后,别名会直接映射到别名所指定的路径;在使用相关模块时,可以使用更加简单的方式书写模块路径
modules
resolve.modules
用于指定webpack解析模块时从哪些目录下搜索模块,默认情况下,webpack只从node_modules
中搜索;如果有一个模块是自己编写的文件,那么它就不需要到
node_modules
中取查找;比如, 我们经常会在src/components
中编写一些组件,为了避免每次引用组件都写很长的路径,就可以通过modules
配置来简化以下路径文件结构
配置
使用
webpack在搜索模块时,会根据
modules
的配置从左到右开始搜索;所以,在使用navMenu
时,会先从./src/components
中查找,如果没有找到,则会去node_modules
中查找;extensions (扩展名配置)
在模块导入语句中没有带文件后缀时,webpack会自动带上尝试后缀去访问(默认带
.js
);resolve.extensions
用于配置尝试访问的后缀列表,列表的优先级为从左到右配置
使用
上面的代码结合配置之后,会先尝试使用
.js
作为后缀去访问index,如果不存在则尝试使用.vue
作为后缀去访问;以此类推,如果配置的后缀都尝试了还没有找到文件,则会报错,文件未找到定义环境变量
在实际开发中,开发环境和线上生产环境往往会使用不同的服务和域名,并且不同的环境应该是可以自动切换的,这就需要在代码中使用环境变量来区分不同的环境,然后自动切换相应的服务和域名
webpack通过内置的
DefinePlugin
插件可以提供了一个区分环境的全局变量DefinePlugin
的键值都是一个标志符或者多个用.
连接起来的标志符如果
value
是一个字符串,它会被当作一个代码片段来使用如果
value
不是字符串,它会被转化为字符串(包括函数)如果
value
是一个对象,它所有的key
会被同样的方式定义(即全局可以直接访问对象的key,value
则会应用DefinePlugin
的键值规则)如果在一个
key
前面加了typeof
,它会被定义为typeof
调用module.exports = {
plugin: {
new webpack.DefinePlugin({
DEV: JSON.stringify('dev')
})
}
}
if (DEV) {
// 开发
BASE_URL: '' // 开发服务
} else {
// 生产
BASE_URL: '' // 生产服务
}
区分环境
通过环境变量可以进行环境的区分,但是代码中如果涉及到多处需要区分且每次都需要手动去配置,这不仅加大了工作量而且还容易出错;
一般的解决方案是将不同环境的配置分开到不同的文件,然后使用
webpack-merge
将其与基础配置进行合并webpack.base.config.js
: 基础配置文件webpack.pro.config.js
: 线上生产环境配置文件webpack.dev.config.js
: 开发环境配置文件webpack-merge
是一个函数,它提供了合并功能,接收一个或多个对象/数组,用于对象的合并与数组的连接,返回合并后的对象;在合并对象时,如果同一个key出现多次,则后面的覆盖前面的安装
配置
webpack.base.config.js
是基础的webpack配置,各个环境可以通用,所以webpack.base.config.js
需要作为merge的第一个参数开发环境的配置
生产环境的配置
配置命令脚本
热更新
热更新主要应用在开发环境,当代码更改之后页面上只更新被修改的部分,不需要刷新页面,对开发和调试非常有利;
webpack中的热更新的配置主要依赖
devServer.hot
以及webpack的内置插件HotModuleReplacementPlugin
以上的代码配置之后在浏览器查看,会发现还是会刷新整个页面;这个时候还需要在入口文件中做以下配置
module.hot
用于通知webpack此模块可以用于热更新,更多信息可以参考官方文档写在最后
通过本篇文章中介绍的webpack配置,可以满足更加个性化的开发需求以及更高效率的开发
本文所对应的配置源码已提交到我的github
END
The text was updated successfully, but these errors were encountered: