【前端工程化】五:webpack5快速入门(二)

陆子石
2023-12-01

webpack-dev-server

要实现在开发时修改了文件可以自动编译展示,可以使用webpack的watch属性为true和vscode的live server插件,即观察模式

// webpack.config.js
module.exports = {
    watch: true,
    ...
}

但是这样做有几个缺点:

  • 所有源代码都会重新编译
  • 因为我们使用了clean-webpack-plugin插件,所以每次编译成功以后都需要进行文件读写操作
  • live server是vscode插件,我们理所应当使用Webpack的插件
  • live server不能实现局部刷新

基于以上原因,所以我们使用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

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端口上')
})

HMR功能使用

模块热替换(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 组件支持热更新

我们在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中的值

Vue 组件支持热更新

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”,

output中的path

publicPath就是告知index.html将来去哪个地方找你想加载的资源

localhost:8080 + ‘/’ + ‘js/main.js’

本地服务器 + publicPath + filename

如果未配置publicPath,那么浏览器会自动拼上’/’,所以可以手动的配置为’/’,但是此时npm run build后的打包文件无法在本地打开;为了在本地打开打包文件可以给publicPath配置’./’,但是此时npm run serve无法运行,因为会从当前目录开始查找js/main.js文件,是找不到的;

devserver中的path

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可以找到打包后的资源;

devServer常用配置

hotOnly: 无论如何不刷新;

compress: 开启g-zip压缩;

historyApiFallback: 当使用browserRoute模式的时候,刷新可能会导致页面404;

proxy代理设置

启动本地服务器时访问接口,会出现跨域问题;

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
        }
    }
},

resolve模块解析规则

文件查找默认有三个机制

  • 相对路径
  • node_modules下
  • 绝对路径

extensions: 文件拓展名,当我们没有写文件后缀名时,会去这里寻找;

alias: 路径别名设置;

resolve: {
    extensions: [".js", ".json", '.ts', '.jsx', '.vue'], 
    alias: {
        '@': path.resolve(__dirname, 'src')
    }
},

source-map作用

modedevelopment时默认devtooleval

模式是: [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

vue中为source-map

react中为cheap-module-source-map

ts-loader编译TS

npm install typescript ts-loader -D

此时ts-loaderbabel-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文件

参考 Vue组件支持热更新 章节;

vue2.0版本+vue-loader15版本总共三步,配置vue-loader,配置vue-loader插件,安装vue-template-compiler插件

 类似资料: