webpack-dev-server
默认情况下,我们开发完成的代码,为了调试,我们需要进行如下操作:
npm run build, 编译相关的代码- 通过live server或者直接通过浏览器,打开index.html代码,查看效果
但是因为这个操作需要重复多次反复执行,所以该操作经常会影响我们的开发效率
我们希望可以做到,当文件发生变化时,可以自动的完成 编译 和 展示, 也就是具备live reloading(实时重新加载)功能
这个时候我们可以使用插件webpack-dev-server
该插件可以在生产阶段,在本地开发一个服务器,为我们提供打包,编译运行和 实时刷新的功能
并且webpack-dev-server 在编译之后不会写入到任何输出文件,而是将 bundle 文件保留在内存中
也就是说webpack-dev-server会通过memory-fs库,将打包后形成的文件直接加载到内存中进行运行,减少了频繁的文件读取和写入操作
npm install webpack-dev-server -D
在安装完对应的插件后,我们需要在packge.json的scripts字段中配置如下命令即可
"serve": "webpack serve"
此时webpack-dev-server 就可以使用默认配置在本地开启对应服务
模块热替换(HMR)
HMR的全称是Hot Module Replacement,翻译为模块热替换
模块热替换是指在 应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面
简而言之 就是只刷新变化的部分,而不是去刷新整个页面,以提高性能,节省开发时间
HMR通过如下几种方式,来提高开发的速度:
- 不重新加载整个页面,这样可以保留某些应用程序的状态不丢失
- 只更新需要变化的内容,节省开发的时间
- 修改了css、js源代码,会立即在浏览器更新,相当于直接在浏览器的devtools中直接修改样式
在不开启HMR的情况下,当我们修改了源代码之后,整个页面会自动刷新,使用的是live reloading
默认情况下,webpack-dev-server已经支持HMR,我们只需要开启即可(默认已经开启)
webpack.config.js
devServer: {
hot: true // => hot的默认值就是true
}
但是此时会发现,当我们修改了某一个模块的代码时,依然是刷新的整个页面
是因为我们需要去指定哪些模块发生更新时,进行HMR
模块文件
export function sum(num1, num2) {
return num1 + num2
}
入口文件
import { sum } from './math'
console.log(sum(10, 20))
// 如果支持HMR 或者 开启HMR
if (module.hot) {
// 参数一 必填项 模块路径
// 参数二 选填项 模块刷新后对应的回调函数
module.hot.accept('./math.js', () => {
console.log("math 模块发生了改变")
})
}
但是我们在开发Vue、React项目,我们修改了组件的时候,往往是希望每一个组件都可以进行热更新
这就需要我们依次手动将对应的模块添加到HMR中,这必然是十分麻烦的
事实上社区已经针对这些有很成熟的解决方案了
比如vue开发中,我们使用vue-loader,此loader支持vue组件的HMR,提供开箱即用的体验
比如react开发中,有react-refresh,实时调整react组件
host配置
host设置主机地址:
- 默认值是localhost
- 如果希望其他地方也可以访问,可以设置为 0.0.0.0
localhost 和 0.0.0.0 的区别:
- localhost:本质上是一个域名,通常情况下会被解析成127.0.0.1
- 127.0.0.1:回环地址(Loop Back Address),表达的意思其实是我们主机自己发出去的包,直接被自己接收
- 正常的数据库包经常 应用层 - 传输层 - 网络层 - 数据链路层 - 物理层
- 而回环地址,是在网络层直接就被获取到了,是不会经常数据链路层和物理层的
- 比如我们监听 127.0.0.1时,在同一个网段下的主机中,通过ip地址是不能访问的
- 0.0.0.0:监听IPV4上所有的地址,再根据端口找到不同的应用程序
- 比如我们监听 0.0.0.0时,在同一个网段下的主机中,通过ip地址是可以访问的
port设置监听的端口,默认情况下是8080
open是否打开浏览器: 默认值是false,设置为true会打开浏览器
compress是否为静态文件开启gzip compression:
- 默认值是false,可以设置为true
- 当设置为true的时候,本地服务会将资源以gzip的方式传递给浏览器
- 浏览器拿到对应的gzip格式文件后,自动进行解压缩并执行
devServer: {
port: 3000,
open: true,
compress: true,
host: '0.0.0.0'
}
区分环境
目前我们所有的webpack配置信息都是放到一个配置文件中的:webpack.config.js
当配置越来越多时,这个文件会变得越来越不容易维护
并且某些配置是在开发环境需要使用的,某些配置是在生成环境需要使用的,当然某些配置是在开发和生成环境都会使用的
所以,我们最好对配置进行划分,通过在不同环境下加载不同的配置文件,来方便我们维护和管理
处理方式一: 编写两个不同的配置文件,开发和生成时,分别加载不同的配置文件即可
修改配置文件所在目录结构
config # 在项目根目录下 新建一个名为config文件夹 专门用于存在不同环境下的配置文件
├── webpack.dev.config.js # 开发环境下 配置文件
└── webpack.prod.config.js # 生产环境下 配置文件
package.json
"scripts": {
"build": "webpack --config ./config/webpack.prod.config.js",
"serve": "webpack serve --config ./config/webpack.dev.config.js"
}
webpack.dev.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { DefinePlugin } = require('webpack')
module.exports = {
// 如果entry配置的是相对路径,那么entry对应的相对路径是不需要因为配置文件路径的改变而变动的
// 因为entry相对的并不是当前文件所在的路径,而是context属性对应的值
// context的值是解析入口(entry point)和加载器(loader)中相对路径对应的相对地址
// 默认情况下context的值是webpack命令启动时所在的文件夹,一般就是项目的根目录
// context: path.resolve(__dirname, './'),
entry: '/src/index.js',
mode: 'development',
output: {
filename: 'bundle.js',
// 因为修改了配置文件所在路径
// 所以需要修改对应的相对路径
path: path.resolve(__dirname, '../build')
},
devServer: {
port: 3000
},
resolve: {
extensions: ['.js', '.json', '.ts', '.json', '.vue', '.jsx', '.tsx'],
alias: {
// 因为修改了配置文件所在路径
// 所以需要修改对应的相对路径
'@': path.resolve(__dirname, '../src')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: '自定义title',
name: 'Klaus',
template: './index.html'
}),
new DefinePlugin({
NAME: "'Klaus'",
AGE: "'23'"
})
]
}
webpack.prod.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { DefinePlugin } = require('webpack')
module.exports = {
entry: '/src/index.js',
mode: 'production',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, '../build'),
clean: true
},
resolve: {
extensions: ['.js', '.json', '.ts', '.json', '.vue', '.jsx', '.tsx'],
alias: {
'@': path.resolve(__dirname, '../src')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: '自定义title',
name: 'Klaus',
template: './index.html'
}),
new DefinePlugin({
NAME: "'Klaus'",
AGE: "'23'"
})
]
}
但是使用这种方式,会发现webpack.dev.config.js和webpack.prod.config.js中存在很多的相同配置
此时我们可以将开发和生产环境中公共的配置抽离到webpack.common.config.js中
然后使用webpack-merge这个第三方库,将我们多个webpack配置文件进行合并
npm i webpack-merge -D
webpack.common.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { DefinePlugin } = require('webpack')
module.exports = {
entry: '/src/index.js',
mode: 'development',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, '../build')
},
resolve: {
extensions: ['.js', '.json', '.ts', '.json', '.vue', '.jsx', '.tsx'],
alias: {
'@': path.resolve(__dirname, '../src')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: '自定义title',
name: 'Klaus',
template: './index.html'
}),
new DefinePlugin({
NAME: "'Klaus'",
AGE: "'23'"
})
]
}
webpack.dev.config.js
const { merge } = require('webpack-merge')
const config = require('./webpack.common')
// 此时如果webpack.dev.config.js和webpack.common.config.js中存在相同的配置
// 那么dev.config.js中的配置会覆盖common.config.js中的配置
module.exports = merge(config, {
devServer: {
port: 3000
}
})
webpack.prod.config.js
const { merge } = require('webpack-merge')
const config = require('./webpack.common')
module.exports = merge(config, {
output: {
clean: true
}
})