要实现在开发时修改了文件可以自动编译展示,可以使用webpack的watch属性为true和vscode的live server插件,即观察模式
// webpack.config.js
module.exports = {
watch: true,
...
}
但是这样做有几个缺点:
基于以上原因,所以我们使用webpack-dev-server, devServer将数据都写在内存中;
npm install webpack-dev-server -D
注意,这里配置的命令与webpack4的yarn webpack-dev-server有所不同
// package.json
"scripts": {
"serve": "webpack serve --config lg.webpack.js"
}
webpack-dev-middleware
是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server。webpack-dev-server
在内部使用了它,然而它也可以作为一个单独的 package 来使用,以便根据需求进行更多自定义设置。下面是一个 webpack-dev-middleware 配合 express server 的示例。
const express = require('express')
const webpackDevMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
const app = express()
// 获取配置文件
const config = require('./webpack.config.js')
const compiler = webpack(config)
app.use(webpackDevMiddleware(compiler))
// 开启端口上的服务
app.listen(3000, () => {
console.log('服务运行在 3000端口上')
})
模块热替换(hot module replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新所有类型的模块,而无需完全刷新。
注意:因为我们在开发阶段配置Mode为development,与我们配置的.browserslistrc文件的兼容性有冲突,官方建议我们使用taget: 'web’的方式来解决;
配置hotOnly属性无论是否更新都不会刷新浏览器。
// webpack.config.js
module.exports = {
target: 'web',
devServer: {
hot: true,
// hotOnly: true
}
...
}
需要在文件中手动配置模块的热更新,如下:
import './title'
if (module.hot) {
module.hot.accept(['./title.js'], () => {
console.log('title.js模块更新')
})
}
此时title.js中发生更新时,会触发console.log(‘title.js模块更新’),并且有热更新的效果;
我们在react中,需要编译jsx语法
npm install @babel/preset-react -D
对于react组件的热更新,官方要求我们要下载两个插件
npm install @pmmmwh/react-refresh-webpack-plugin react-refresh -D
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env'],
['@babel/preset-react'],
],
plugins: [
['react-refresh/babel']
]
}
// webpack.config.js
...
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
module.exports = {
...,
target: 'web',
devServer: {
hot: true
},
module: {
rules: [
...,
{
test: /\.jsx?$/,
use: ['babel-loader']
}
]
},
plugins: [
...,
new ReactRefreshWebpackPlugin()
]
}
问题? React组件热更新时,丢失了input中的值
npm install vue-template-compiler vue-loader vue -D
vue2.0版本+vue-loader15版本总共三步,配置vue-loader,配置vue-loader插件,安装vue-template-compiler插件;
编译.vue语法,需要配置vue-loader和vueLoaderPlugin插件
解决Cannot read property 'parseComponent' of undefined
报错需要安装vue-template-compiler
参考资料: https://vue-loader.vuejs.org/zh/guide/#vue-cli
...
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
...,
target: 'web',
devServer: {
hot: true
},
module: {
rules: [
...,
{
test: /\.vue$/,
use: ['vue-loader']
}
]
},
plugins: [
...,
new VueLoaderPlugin()
]
}
// “vue-template-compiler”: “^2.6.14”,
publicPath就是告知index.html将来去哪个地方找你想加载的资源
localhost:8080 + ‘/’ + ‘js/main.js’
本地服务器 + publicPath + filename
如果未配置publicPath,那么浏览器会自动拼上’/’,所以可以手动的配置为’/’,但是此时npm run build后的打包文件无法在本地打开;为了在本地打开打包文件可以给publicPath配置’./’,但是此时npm run serve无法运行,因为会从当前目录开始查找js/main.js文件,是找不到的;
devserver运行的时候,会将文件打包在一个虚拟目录下,output的publicPath配置了index.html中去寻找打包后路径(此时未经webpack打包的资源在虚拟目录下找不到,需要配置contentBase),devServer中的publicPath配置了devServer去哪个目录寻找,devServer中的contentBase配置了index.html中未经webpack打包但引入了的资源路径;
publicPath:就是告知devServer应该去哪个目录下面找,即打包后的文件会放在一个虚拟的’/lg’(publicPath)目录下;
默认值为‘/’,强烈建议output中的publicPath和devServer的publicPath设置为一样的值;配置以后,应该访问http://localhost:8080/lg;
contentBase: 我们打包之后的资源如果说依赖了其他的资源,此时就告知去哪找(比如在index.html文件中引入了未经webpack处理的文件,此时告诉index.html这些依赖的资源去哪里找,一般用绝对路径);
watchContentBase: 与contentBase配套使用,默认值为false,当我们修改了打包后资源依赖的资源,页面会随着发生变化,默认是修改了资源页面不会及时更新的;
output: {
filename: 'js/main.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/lg'
},
target: 'web',
devServer: {
hot: true,
publicPath: '/lg',
// contentBase: path.resolve(__dirname, 'public'),
// watchContentBase: true,
open: true
},
此时http://localhost:8080访问不到,但是访问http://localhost:8080/lg可以访问到页面,http://localhost:8080/lg/js/main.js可以找到打包后的资源;
hotOnly: 无论如何不刷新;
compress: 开启g-zip压缩;
historyApiFallback: 当使用browserRoute模式的时候,刷新可能会导致页面404;
启动本地服务器时访问接口,会出现跨域问题;
pathRewrite: 重写路径;
changeOrigin: 修改host主机名;
devServer: {
hot: true,
hotOnly: true,
port: 4000,
open: false,
compress: true,
historyApiFallback: true,
proxy: {
// /api/users
// http://localhost:4000/api/users
// https://api.github.com/info/users
// /api/users---> 返回
'/api': {
target: 'https://api.github.com',
pathRewrite: { "^/api": "" },
changeOrigin: true
}
}
},
文件查找默认有三个机制
extensions: 文件拓展名,当我们没有写文件后缀名时,会去这里寻找;
alias: 路径别名设置;
resolve: {
extensions: [".js", ".json", '.ts', '.jsx', '.vue'],
alias: {
'@': path.resolve(__dirname, 'src')
}
},
mode为development时默认devtool为eval
模式是: [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
vue中为source-map
react中为cheap-module-source-map
npm install typescript ts-loader -D
此时ts-loader和babel-loader都可以使用,但是ts-loader不可以plyfill;
babel-loader并不能对数据类型校验,语法有误也会被直接打包成功,但是我们使用ts-loader时会在打包的时候校验出来,让我们更早的发现问题;
官方建议:只是转换语法,使用ts-loader就行了,但是如果我们需要使用polyfill时,可以使用babel-loader;
使用babel-loader,可以先做一个校验,在package.json中新增ts语法校验,tsc --noEmit
可以只校验,不打包成js文件;这样在执行num run build
之前就会帮我们校验ts语法,如果有误则不会打包了;
// package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "npm run ck && webpack",
"serve": "webpack serve",
"ck": "tsc --noEmit"
},
module: {
rules: [
{
test: /\.jsx?$/,
use: ['babel-loader']
},
{
test: /\.ts$/,
use: ['babel-loader']
}
]
},
// babel.config.js 配置了polyfill,配置了ts解析
module.exports = {
presets: [
['@babel/preset-env', {
useBuiltIns: 'usage',
corejs: 3
}],
['@babel/preset-typescript']
]
}
参考 Vue组件支持热更新 章节;
vue2.0版本+vue-loader15版本总共三步,配置vue-loader,配置vue-loader插件,安装vue-template-compiler插件;