构建基础 - web-dev-server

446 阅读6分钟

webpack-dev-server

默认情况下,我们开发完成的代码,为了调试,我们需要进行如下操作:

  1. npm run build, 编译相关的代码
  2. 通过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通过如下几种方式,来提高开发的速度:

  1. 不重新加载整个页面,这样可以保留某些应用程序的状态不丢失
  2. 只更新需要变化的内容,节省开发的时间
  3. 修改了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 的区别:

  1. localhost:本质上是一个域名,通常情况下会被解析成127.0.0.1
  2. 127.0.0.1:回环地址(Loop Back Address),表达的意思其实是我们主机自己发出去的包,直接被自己接收
    • 正常的数据库包经常 应用层 - 传输层 - 网络层 - 数据链路层 - 物理层
    • 而回环地址,是在网络层直接就被获取到了,是不会经常数据链路层和物理层的
    • 比如我们监听 127.0.0.1时,在同一个网段下的主机中,通过ip地址是不能访问的
  3. 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.jswebpack.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
  }
})