编程崽

登录

一叶在编程苦海沉沦的扁舟之上,我是那只激情自射的崽

webpack 基础使用配置 + 版本5的报错

webpack 基础使用配置 + 版本5的报错

webpack官网

前言

现代前端开发,都已经开始基于各种框架来开发,比如 vue、react 等等,即使不使用框架,我们写原始的 htm + css + js,也会经常想使用便捷的开发语法,比如使用 scss 或 less 开发css,使用 ES6+ 版本的 js 语法。

要知道,无论是框架的文件目录结构,还是 scss、less、ES6等,浏览器要么是无法支持,要么是有严重的兼容性问题,需要进一步处理后,生成常规的静态的原始的,且浏览器能兼容的 html + css + js。

这就需要一个用来编译的工具,webpack 就是当前最流行的前端编译工具,和他类似的工具还有 gulp、vite 等等,但 webpack 统治前端编译界时间最长,口碑也相当不错。

我现在就来整理一下 webpack 的一些基本使用。

webpack 基于 nodejs,使用是需要用 npm 安装。

最基本的 webpack

这是 webpack 的本体,不安装就没有 webpack,无法使用。

依赖:

  • webpack
  • webpack-cli
sh 复制代码
npm install -D webpack webpack-cli

再创建一个文件 webpack.config.js,作为 webpack 编译的配置文件,一下给了注释说明:

js 复制代码
const path = require('path');

module.exports = {
  // 编译的入口文件配置,会取这些文件来编译
  entry: {
    'main': './src/main.js',
    'util': './src/util/index.js',
    'component': './src/component/index.js',
  },
  // entry: './src/main.js', // 如果只用编译一个文件,可以直接写文件地址

  // 出口文件配置,编译完后文件的去向
  output: {
    // 编译完成后放入这个文件夹
    path: path.resolve(__dirname, 'dist'),

    // 编译完成后,生成的文件名
    // [name] 表示 entry 配置中,某个文件前面的 key
    // [hash] 表示生成的一个随机 hash,可以省略
    // 比如:
    // entry 中配置了: 'myName': './src/main.js
    // 那么最终生成的文件名,是: myName.8413e4f0204915bc9c29.js 这种形式的
    filename: '[name].[hash].js',
  },
};

安装完成后,在 package.json 写下 build 指令:

js 复制代码
"scripts": {
  "build": "webpack-cli --mode production --config ./webpack.config.js"
},

之后就可以使用 npm run build ,来使用 webpack 编译 js 了。

这一条编译指令中:

  • webpack-cli:执行 webpack-cli。
  • --mode production :使用 production 模式来编译,支持的值:development | production | none,具体配置区别请查阅 mode 配置,这一项必写,但可以不写在这里,可以写在 webpack 配置文件中,写作 key value。
  • --config ./webpack.config.js:使用的编译配置文件,如果不写,默认为 ./webpack.config.js

这个配置文件,因为没有任何有实际意义的配置,比如使用插件啦、使用loader啦,所以编译完成的文件,看上去内容和源文件可能不一样,但其实执行效果是一模一样的。

ES6+ 转译工具 babel

依赖:

  • babel-loader:允许使用 Babel 和 webpack 传输 JavaScript 文件。
  • @babel/core:Babel 的核心,必须的。
  • @babel/preset-env:用来编译语法和js新api的插件。
  • core-js:配置 corejs: 3 需要的,编译新api时需要使用的库。
  • babel-preset-es2015:已被取代
  • babel-preset-stage-2:已被取代

指令指令安装依赖。

sh 复制代码
npm install -D babel-loader @babel/core @babel/preset-env core-js

修改 webpack.config.js,添加 babel 配置:

js 复制代码
const path = require('path');

module.exports = {
  entry: {
    'main': './src/main.js',
    'util': './src/util/index.js',
    'component': './src/component/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[hash].js',
  },
  // 配置模块
  module: {
    rules: [ // 定义规则
      {
        test: /\.js$/, // 命中这条规则的文件们,是 js 格式的文件需要使用
        include: [path.resolve(__dirname, 'src')], // 包含的文件,不设置就是所有用到的文件都编译
        // exclude: /node_modules/, // 忽略的文件们,不写则无
        loader: 'babel-loader', // 使用 loader
      },
    ],
  },
};

添加 .babelrc 文件:

json 复制代码
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage", // "usage": 按需引入
        "bugfixes": true, // 一项优化,默认为false,Babel 8版本将默认为true
        "corejs": 3 // 指定要使用的 core-js 版本,建议 3
      }
    ]
  ]
}

启动 webpack 开发服务器 devServer

开发时,不希望每次改了代码,都要重新打包,这个依赖可以启动一个 webpack 服务器,实时编译,保存编译更新。

依赖:

  • webpack-dev-server:服务开启
sh 复制代码
npm install -D webpack-dev-server

添加完成后,可以修改 package.json 中的指令了:

js 复制代码
"scripts": {
  "start": "npm run dev",
   // 用于启动开发环境服务
  "dev": "webpack serve --mode development --config ./webpack.config.js",
   // 用于打包
  "build": "webpack-cli --mode production --config ./webpack.config.js"
},

修改 webpack.config.js

js 复制代码
const path = require('path');

module.exports = {
  entry: {
    'main': './src/main.js',
    'util': './src/util/index.js',
    'component': './src/component/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
  },
  devServer: {
    publicPath: '/dist/', // 这个服务的根路由
    // open: true, // 自动打开浏览器
    // openPage 配置自动打开时,打开的路由
    // 但是,由于上面我们使用的 output 的 filename 是 [name].[hash].js,使用了 随机 hash
    // 这里无法得知 hash 的值,无法准确的打开 main.js
    // 这个功能其实主要是开发 html 时,自动打开的
    // openPage: 'dist/main.js',
    // host: '192.168.0.104', // 服务器监听的地址
    port: 8098, // 设定服务的端口
    hot: true, // 热替换
    
    // proxy 代理
    proxy: {
      // 设置完此代理后,当我们访问 http://localhost:8098/a/main.js,会走一下代理,最终效果相当于访问 http://localhost:8098/dist/main.js
      // 更多教程,搜索 webpack proxy 查询
      '/a': { // 命中 /a 路由
        target: 'http://localhost:8098', // 代理到这个域名下
        logLevel: 'debug', // 每次代理,都打印代理日志
        pathRewrite: { // 路由替换
          '^/a': '/dist', // 把 /a 替换为 /dist
        },
      }
    },
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: [path.resolve(__dirname, 'src')],
        loader: 'babel-loader',
      },
    ],
  },
};

而且,由于开发环境和正式打包时,配置可能不同,我们可以给测试环境和正式环境设置不同的 webpack 配置文件,来差异化处理。

设置 js 引用文件时的路径别名 resolve

js 复制代码
const path = require('path');

module.exports = {
  entry: {
    'main': './src/main.js',
    'util': './src/util/index.js',
    'component': './src/component/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: [path.resolve(__dirname, 'src')],
        loader: 'babel-loader',
      },
    ],
  },
  // 配置全局路径变量
  resolve: {
    extensions: ['.js'], // 引入可以不加后缀名
    alias: {
      '@': path.join(__dirname, './src'),
    },
  },
};

设置完 resolve 后,内部 js 引入文件时,就可以使用了,比如:

js 复制代码
// src/view/home.js 中,一下两个引入 math 的路径写法是等效的
import math from '@/util/math';

import math from '../util/math';

启动服务或打包时,插入常量

有种情况,比如网站部署后,我们想知道这个网站被打包生成的具体时间,这就需要我们在打包时,把当前打包的时间提供给网站,供网站读取。

且无论何时打开网站,读取到的都是我们打包时塞入的那个时间。

此时就可以使用 webpack.DefinePlugin 这个插件配置一下了:

js 复制代码
const path = require('path');
const webpack = require('webpack'); // 引入 webpack

module.exports = {
  entry: {
    'main': './src/main.js',
    'util': './src/util/index.js',
    'component': './src/component/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: [path.resolve(__dirname, 'src')],
        loader: 'babel-loader',
      },
    ],
  },
  plugins: [
    // 插入编译后代码中的全局变量
    new webpack.DefinePlugin({
      // 注意,这里的值都需要用 JSON.stringify() 转译一下,否则会报错
      'NAME': JSON.stringify('我的项目'),
      'BUILD_TIME': JSON.stringify(Date.now()),
      'MY_INFO': JSON.stringify({age: 18}),
    }),
  ],
};

然后,在项目中任何一个文件中,都可以读取这些常量:

js 复制代码
// src/main.js 中

console.log(NAME) // 我的项目 String
console.log(BUILD_TIME) // 1616748873584 Number
console.log(MY_INFO.age) // 18 Number

使用 source-map

使用webpack打包项目,当我们调试时,会发现在浏览器中看到的源码,是已经被 webpack 打包编译过的、一个文件只有一行的代码,根本不能清晰的调试。

这时就需要 source-map 登场了,它能在编译时,同时生成编译后和编译前的代码对应关系,当我们使用浏览器调试时,他们展示我们实际的代码。

需要使用 devtool 字段,默认情况无 source-map,可以给他赋值不同的值,来生成不同的 source-map,有的 source-map 编译快,有的体积小。。。值很多。

推荐开发时设置 eval-cheap-module-source-map,打包时设置值为 source-map

js 复制代码
const path = require('path');

module.exports = {
  entry: {
    'main': './src/main.js',
    'util': './src/util/index.js',
    'component': './src/component/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js', // 这里不能使用 hash 了,否则下面 openPage 无法正常执行
  },
  // 建议开发环境使用
  // devtool: 'eval-cheap-module-source-map',
  // 建议打包时使用
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.js$/,
        include: [path.resolve(__dirname, 'src')],
        loader: 'babel-loader',
      },
    ],
  },
};