likes
comments
collection
share

构建webpack5.x 知识体系:5、基础之js优化

作者站长头像
站长
· 阅读数 68

这是一个系列文的分享记录,本篇主要是webpack的基础之js优化,js的兼容性处理,如何通过webpack把浏览器不兼容的一些ES6/ES7,React的JSX转义为ES5识别,如何在开发环境中为了统一风格使用ESLint代码校验,HTML压缩,以及webpack5中其他的配置介绍如resolve、noParse、IgnorePlugin、output.library、output. libraryTarget等。

正式开始:

预备技能

基本nodejs知识和npm指令

注意:版本问题导致不同,本系列实战中用到的webpack版本是:"webpack": "^5.68.0",

"webpack-cli": "^4.9.2",

JS兼容性处理

使用babel-loader来处理js的兼容性问题

Babel其实是一个编译JavaScript的平台,可以把ES6/ES7,React的JSX转义为ES5

1、如果只是基础的js兼容性,我们只需要(promise不能转换):在babel-loader处加入presets设置,用@babel/preset-env来做一些常规的语法兼容

2、全部兼容性处理:用@babel/runtime-corejs3和@babel/plugin-transform-runtime 之前webpack4之前使用 @babel/polyfill (暴力解决) :

这种的不好的地方是: 我只要解决部分兼容性问题,但是会将所有兼容性代码都全部引入,体积太大了~

3、需要做兼容性处理的就要做:按需加载 使用---》core-js

所以需要安装: @babel/core和babel-loader @babel/present-env

安装依赖

npm i babel-loader @babel/core @babel/preset-env @babel/preset-react  -D
npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties @babel/plugin-proposal-private-property-in-object  @babel/plugin-proposal-private-methods -D

2、修改配置文件webpack.config.js

 {
                    test: /.jsx?$/,
                    exclude: /node_modules/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                ['@babel/preset-env', {
                                    // 按需加载
                                    useBuiltIns: 'usage',
                                    // 指定core-js版本
                                    corejs: {
                                        version: 3
                                    },
                                    // 指定兼容性做到哪个版本浏览器
                                    targets: {
                                        chrome: '60',
                                        firefox: '60',
                                        ie: '9',
                                        safari: '10',
                                        edge: '17'
                                    }
                                }], '@babel/preset-react'
                            ],
                            plugins: [
                                ["@babel/plugin-proposal-decorators", { legacy: true }],
                                ["@babel/plugin-proposal-private-property-in-object", { "loose": true }],
                                ["@babel/plugin-proposal-private-methods", { "loose": true }],
                                ["@babel/plugin-proposal-class-properties", { loose: true }],
                            ]
                        }
                    }

                },

./src/index.js 修改

// ./src/index/js
import dataJson from './data.json';
import './index.css';
import './index.less';
import './index.scss';

import './assets/icons/iconfont.css';
// 引入样式文件
function add(x, y) {
    return x + y;
}
console.log(add(1, 2));
console.log(dataJson, 'dataJson');

// js中引入图片
const logo = require('./assets/images/vite.png');

const img = new Image();
img.src = logo;
document.body.appendChild(img);



class Person {
    constructor() {
        this.name = 'hello'
    }
    setName(name) {
        this.name = name
    }
}

let p1 = new Person();
p1.setName('webpack5 系列');
console.log(p1.name)

const total = [1, 2, 3, 4].reduce((a, b) => a + b)
console.log('total', total)

新增jsconfig.json

{
    "compilerOptions": {
        "experimentalDecorators": true
    }
}

运行webpack

之前在IE11以下不支持数组的reduce方法,这次也兼容了

ESLint代码校验

语法检查 eslint

rule:cloud.tencent.com/developer/c…

ESlint 语法检测配置说明

安装 eslint-webpack-plugin:(该插件使用 eslint 来查找和修复 JavaScript 代码中的问题。)

注意: 只检查自己写的源代码,第三方库时不用检查的

注意: 如果未安装 eslint >= 7 ,你还需先通过 npm 安装:

1、安装配置 eslint-webpack-plugin

1)安装 eslint-webpack-plugin:(该插件使用 eslint 来查找和修复 JavaScript 代码中的问题。)

npm install eslint-webpack-plugin --save-dev 注意: 如果未安装 eslint >= 7 ,你还需先通过 npm 安装:

npm install eslint --save-dev

2)配置: webpack.config.js文件中:

const ESLintPlugin = require('eslint-webpack-plugin');

plugins: [
    new ESLintPlugin({
      exclude:'node_modules', //排除node_modules文件夹的eslint检查
      fix:true //自动修复js格式错误
    })
  ],

设置检查规则

js风格指南库:airbnb

设置插件规则:eslint-config-airbnb-base:可参考官方文档:www.npmjs.com/package/esl…

默认导出包含了所有的ESLint规则,包括ECMAScript 6+。它需要eslint和eslint-plugin-import

eslint-import-resolver-webpack(如果在webpack.config.js中配置了alias 并且在import时使用了别名需要安装这个)

使用

1、安装

npm i eslint-config-airbnb-base eslint-plugin-import @babel\eslint-parser --save-dev

2)配置:两种方式:

方式一: .eslintrc.js文件中添加:

module.exports = {"extends": "airbnb-base"
}

方式二:package.json文件中添加: // 继承airbnb风格,具体可在github中的airbnb仓库中查看详细风格

"eslintConfig": { "extends": "airbnb-base"}

运行webpack

构建webpack5.x 知识体系:5、基础之js优化

出现以下警告,warning Unexpected console statement no-console,可在js文件中有console.log()输出语句的上方,添加// eslint-disable-next-line,即可忽略该输出语句的警告提示。

no-indef 更改

package.json文件中添加: // 继承airbnb风格,具体可在github中的airbnb仓库中查看详细风格

"eslintConfig": {
        "extends": "airbnb-base",
        "env": {
            "browser": true
        }
    }

.eslintrc.js文件中添加:

module.exports = {
    "parser": "@babel/eslint-parser",
    "extends": "airbnb-base",
    "parserOptions": {
        "ecmaVersion": 2015,
        "requireConfigFile": false,
        "ecmaFeatures": {
            "experimentalObjectRestSpread": true,
            "impliedStrict": true,
            "classes": true
        }
    },
    "rules": {
        "semi": "error",
        "no-console": "off",
        "linebreak-style": "off",
        "eol-last": "off"
            //"indent":["error",2]
    },
    "env": {
        "browser": true,
        "node": true
    }
}

在编译不会报错了,使用的.eslintrc.js 因为也添加了 关闭报错 "no-console"

不使用babel-eslint,已弃用的babel-eslint解析器不支持ES6模块,可以尝试更新

安装了 react 还需要安装

npm i eslint-plugin-react eslint-plugin-react-hooks and eslint-plugin-jsx-a11y -D

ps:注意我们不再使用eslint-loader 使用 eslint-webpack-plugin 代替就可以。

js压缩

生产环境下会自动压缩js 我们看前面概念介绍可以看到 mode:'production',会自动启动一系列的插件,其中就有自动压缩

使用

1、安装

npm i terser-webpack-plugin -D

2、修改webpack.config.js

引用

const TerserPlugin = require('terser-webpack-plugin');

使用

optimization: {
            minimizer: [
                new CssMinimizerWebpackPlugin(),
                new TerserPlugin(),
            ],
            minimize: true, // 如果还想在开发环境下启用 CSS 优化,请将 optimization.minimize 设置为 true:
        },

html压缩

还是之前的HtmlWebpackPlugin添加相关属性就可以

修改webpack.config.js 中 new HtmlWebpackPlugin 添加minify

 new HtmlWebpackPlugin({
            // 复制 './index.html' 文件并自动会引入打包输出的所有资源(js/css)
            template: './index.html',
            minify: {
                // 移除空格
                collapseWhitespace: true,
                // 移除注释
                removeComments: true
            }
        }),

运行webpack

查看build\index.html 中代码压缩

构建webpack5.x 知识体系:5、基础之js优化

图片优化

1、我们也把图片放到一个文件夹下

 {
                    // 处理图片 
                    // 问题:默认处理不了html中img图片
                    test: /.(jpe?g|png|gif|svg)$/,
                    type: "asset",

                    generator: {
                        //与output.assetModuleFilename是相同的,这个写法引入的时候也会添加好这个路径
                        filename: 'imgs/[name].[hash:6][ext]'
                    },
                }

运行webpack

查看build文件夹情况,发现多了一个imgs文件夹,里边存放这我们引入的所有的图片

构建webpack5.x 知识体系:5、基础之js优化

查看浏览器也能正常运行

2、我们还可以更改output

output: {
        // 输出名 string (default) entry chunk 的文件名模板
        filename: 'js/[name].[contenthash:10].js',
        // 输出路径 __dirname nodejs变量 代表当前文件的目录绝对路径  必须是绝对路径(使用 Node.js 的 path 模块)
        path: resolve(__dirname, 'build'),
        assetModuleFilename:'imgs/[contenthash:6][ext]'
    }

ps:第一种和第二种的方式的优先级是怎么样的啊?

在loader中的generator中设置的

generator: {
                        //与output.assetModuleFilename是相同的,这个写法引入的时候也会添加好这个路径
                        filename: 'imgs/[name].[hash:6][ext]'
                    }

优先级就更高一点

3、我们知道一般当图片小于一定大小的时候,使用base64图片,减少请求

{
                    // 处理图片 
                    // 问题:默认处理不了html中img图片
                    test: /.(jpe?g|png|gif|svg)$/,
                    type: "asset",

                    // //解析
                    parser: {
                        //转base64的条件
                        dataUrlCondition: {
                            maxSize: 25 * 1024, // 25kb
                        }
                    },
                    generator: {
                        //与output.assetModuleFilename是相同的,这个写法引入的时候也会添加好这个路径
                        filename: 'imgs/[name].[hash:6][ext]'
                    },
                }

如果type: "asset/inline", 那么资源会以内联的形式加载打包进去,但是不回现在图片的大小都会以这种base64的形式,也会导致html体积过大。

type: "asset", 是自动的,默认会判断资源的大小,当资源文件大于8k,就当asset/resource, 小于就是"asset/inline"

运行webpack

查看build/imgs文件夹情况,发现少了一个图片文件

构建webpack5.x 知识体系:5、基础之js优化

查看浏览器

构建webpack5.x 知识体系:5、基础之js优化

少的图片因为小于我们设置的大小,以base64格式显示。

resolve

1、extensions

指定extension之后可以不用在require或是import的时候加文件扩展名,会依次尝试添加扩展名进行匹配

webpack 就会按照 extensions 配置的数组从左到右的顺序去尝试解析模块

resolve: {
  extensions: [".js",".jsx",".json",".css"]
}

需要注意的是:

  1. 高频文件后缀名放前面;
  2. 手动配置后,默认配置会被覆盖 如果想保留默认配置,可以用 ... 扩展运算符代表默认配置,例如
resolve: {
 extensions: ['.js', '...'], 
}

2、alias

配置别名可以加快webpack查找模块的速度

resolve:{
    // 配置别名
    alias: {
      '@': resolve('src'),
      'components': resolve('src/components'),
    }
  }

使用

// 使用 src 别名 @ 
import '@/index.css'

3 、modules

配置 webpack 去哪些目录下寻找第三方模块,默认情况下,只会去 node_modules 下寻找,如果你我们项目中某个文件夹下的模块经常被导入,不希望写很长的路径,那么就可以通过配置 resolve.modules 来简化。

  • 对于直接声明依赖名的模块(如 react ),webpack 会类似 Node.js 一样进行路径搜索,搜索node_modules目录

  • 这个目录就是使用

    resolve.modules
    

    字段进行配置的 默认配置

    resolve: {
        modules: ['node_modules'],
    }
    

    如果可以确定项目内所有的第三方依赖模块都是在项目根目录下的 node_modules 中的话

    resolve: {
        modules: [resolve(__dirname, 'node_modules')],
    }
    

4、 mainFields

有一些第三方模块会提供多份代码,例如 bootstrap,可以查看 bootstrappackage.json 文件:

{
    "style": "dist/css/bootstrap.css",
    "sass": "scss/bootstrap.scss",
    "main": "dist/js/bootstrap",
}

resolve.mainFields 默认配置是 ['browser', 'main'],即首先找对应依赖 package.json 中的 brower 字段,如果没有,找 main 字段。

如:import 'bootstrap' 默认情况下,找得是对应的依赖的 package.jsonmain 字段指定的文件,即 dist/js/bootstrap

假设我们希望,import 'bootsrap' 默认去找 css 文件的话,可以配置 resolve.mainFields 为:

module.exports = {
    //....
    resolve: {
        mainFields: ['style', 'main'] 
    }
}

当目录下没有 package.json 文件时,我们说会默认使用目录下的 index.js 这个文件,其实这个也是可以配置的

resolve: {
  mainFiles: ['index'], // 你可以添加其他默认使用的文件名
},

ps:默认情况下package.json 文件则按照文件中 main 字段的文件名来查找文件

resolve: {
  // 配置 target === "web" 或者 target === "webworker" 时 mainFields 默认值是:
  mainFields: ['browser', 'module', 'main'],
  // target 的值为其他时,mainFields 默认值为:
  mainFields: ["module", "main"],
}

5、resolveLoader

一般情况下保持默认配置就可以了,但如果你有自定义的 Loader 就需要配置一下,不配可能会因为找不到 loader 报错

noParse

  • module.noParse 字段,可以用于配置哪些模块文件的内容不需要进行解析
  • 不需要解析依赖(即无依赖) 的第三方大型类库等,可以通过这个字段来配置,以提高整体的构建速度

使用 noParse 进行忽略的模块文件中不能使用 import、require、define 等导入机制

module.exports = {
// ...
module: {
  noParse: /jquery|lodash/, // 正则表达式
  // 或者使用函数
  noParse(content) {
    return /jquery|lodash/.test(content)
  },
}
}

IgnorePlugin

IgnorePlugin用于忽略某些特定的模块,让 webpack 不把这些指定的模块打包进去

1 src/index.js

import moment from  'moment';
import 'moment/locale/zh-cn'
console.log(moment().format('MMMM Do YYYY, h:mm:ss a'));

2 webpack.config.js

import moment from  'moment';
console.log(moment);
      new webpack.IgnorePlugin({
          //A RegExp to test the context (directory) against.
          contextRegExp: /moment$/,
          //A RegExp to test the request against.
          resourceRegExp: /^./locale/
      new MiniCSSExtractPlugin({
          filename:'[name].css'
      })
  • 第一个是匹配引入模块路径的正则表达式
  • 第二个是匹配模块的对应上下文,即所在目录名

跨域、服务器代理

我们知道在开发阶段,我们调用其他的第三方的api接口,如果不同域就会出现跨域,我们就无法开发下了。也不可能永远使用mock数据,还是要真实的接口进行前后端的联调。

更改devServer

 // 开发服务器 devserver 用来自动化(自动编译 自动打开浏览器 自动刷新浏览器。。。)
        // 特点 指挥在内存中编译打包 不会有任何输出
        // 启动devServer 指令 为npx webpack-dev-server
        // 1、下载依赖 2、使用 启动devServer 指令 为npx webpack-dev-server
        devServer: {
            // npx webpack-dev-server 报错 把 contentBase改为 static 或者注释掉 /webpack5中不用配置contentBase了,注释掉后正常打包
            static: resolve(__dirname, 'build'),
            // 启动gzip压缩
            compress: true,
            // 端口号
            port: 8888,
            // 自动打开浏览器
            open: true,
            // 开启HMR功能
            hot: true,
            // 域名
            host: 'localhost',
            // 服务器代理 -->解决开发环境跨域问题
            proxy: {
                '/api': {
                    target: 'http://localhost:5000',
                    pathRewrite: {
                        '^/api': ''
                    }
                }
            }
        }


onBeforeSetupMiddleware

  • onBeforeSetupMiddleware 在 webpack-dev-server 静态资源中间件处理之前,可以用于拦截部分请求返回特定内容,或者实现简单的数据 mock。

    devServer: {
    onBeforeSetupMiddleware(devServer){// express()
        devServer.app.get('/api/users', (req, res) => {
          res.json([{ id: 1 }, { id: 2 }]);
        });
    }
    }
    

webpack-dev-middleware

webpack-dev-middleware就是在 Express 中提供 webpack-dev-server 静态服务能力的一个中间件

npm install webpack-dev-middleware --save-dev
const express = require('express');
const app = express();
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackOptions = require('./webpack.config');
webpackOptions.mode = 'development';
const compiler = webpack(webpackOptions);
app.use(webpackDevMiddleware(compiler, {}));
app.listen(3000);
  • webpack-dev-server 的好处是相对简单,直接安装依赖后执行命令即可
  • 而使用webpack-dev-middleware的好处是可以在既有的 Express 代码基础上快速添加 webpack-dev-server 的功能,同时利用 Express 来根据需要添加更多的功能,如 mock 服务、代理 API 请求等

感谢

到此,本篇介绍到此结束,之后将陆续整理 webpack 知识体系的内容分享,尽情期待,感谢您的阅读~~

转载自:https://juejin.cn/post/7069592497155997709
评论
请登录