阅读 158

Webpack5不完全指南-进阶篇

处理HTML中的静态资源

前面处理了css中的图片资源,webpack默认不会对HTML进行解析,当然也不会收集处理HTML中的图片等资源,html-withimg-loader可以解析HTML中图片资源收集到 dist/img

  • 安装
    npm i html-withimg-loader -D

  • 配置loader

    // webpack.config.js   module: {   rules: [     {       test: /\.(htm|html)$/i,       loader: "html-withimg-loader"     }   ]   复制代码

多页面应用打包

基于MVVM的开发的web应用多为单页面应用,但是某些场景更需要多页面应用,例如OA办公系统等,webpack也可以实现多页面应用打包。

  • 在src下新建入口文件 main.jsother.js

    // src/main.js   console.log("这是main.js") // src/other.js   console.log("这是other.js") 复制代码

  • 配置入口和出口

    // webpack.custom.config.js   module.exports = {   // entry: "./src/index.js",   // 1. 当为多组入口出口时 传入对象   entry: {     // 2. 注册多个入口文件     index: "./src/main.js",     other: "./src/other.js",   },   // 项目出口配置   output: {     path: path.resolve(__dirname, "./dist"),     // filename: 'bundle.js'     // 3. 当为多个出口文件时 文件名要用 name 变量 否则会报错     filename: '[name].js'   } }, 复制代码

  • 执行 npm run build 打包项目
    image.png
    image.png

配置多个html页面

前面只是配置了多个入口和出口,所以依赖最终还是在一个html这里我们还需要拆分多个html

  • 新建 src/other.html

  • 配置插件

      plugins: [   // 新建一个HtmlPlugin实例   new HtmlPlugin(     {       // 打包输出文件名       filename: "index.html",       // 被复制的目标html       template: "./src/index.html",       // 默认引入所有chunks(index.js和other.js),指定要引入的chunks       chunks: ["index"]     }   ),   new HtmlPlugin(     {       filename: "other.html",       template: "./src/other.html",       // 默认引入所有chunks(index.js和other.js),指定要引入的chunks       chunks: ["other"]     }   ), ] 复制代码

  • npm run build 重新打包

image.png

第三方库的引入方式

webpack的原理是将每个模块作为闭包来进行打包,局部模块导入的库只是局部的变量无法全局使用,可以借助 expose-loader 实现全局作用域变量注入,也可以通过内置插件 webpack.ProvidePlugin 对每个闭包作用域注入变量,自动加载模块

示例

以jquery为例

  • 安装
    npm i jquery

  • main.js 中使用jquery

    console.log("这是main.js"); import $ from 'jquery' // 在当前作用域导入并访问JQ console.log("JQ", $); // 访问全局作用域下的JQ console.log("window.jquery", $); $("body").css("backgroundColor", "green") 复制代码

  • npm run build重新构建后打开 index.html

    image.png 正如之前所讲,使用webpack,在当前模块导入的库只能作为局部变量使用。

expose-loader

  • 安装
    npm i expose-loader -D

  • 配置loader

    // webpack.custom.config.js module: {   rules: [     // 注入全局变量     {       // require.resolve 解析jquery库的绝对路径       test: require.resolve('jquery'),       loader: 'expose-loader',       options: {         // 使用变量 $ 挂载JQ         exposes: '$',       }     }   ] }, 复制代码

  • npm run build 重新打包,可以访问到全局变量 $

    image.png

webpack.ProvidePlugin

ProvidePlugin是webpack内置插件,可以为每个模块注入变量

  • 修改 main.jsother.js,在 other.js 不导入直接使用JQ

    // main.js console.log("这是main.js"); // other.js console.log("这是other.js"); $("body").css("backgroundColor", "pink") 复制代码

  • 配置plugin

    plugins: [   new HtmlPlugin(     {       filename: "index.html",       template: "./src/index.html",       // 1.index.html 引入所有js       chunks: ["index", "other"]     }   ),   // 2.使用 Webpack.ProvidePlugin 为每个模块注入局部变量   new Webpack.ProvidePlugin({     // 每个模块导入jquery模块,并挂载到$、jQuery变量上     $: "jquery",     jQuery: "jquery"   }) ] 复制代码

区分环境配置文件

项目开发时,一般需要使用两套配合文件,开发阶段打包(不压缩代码、不优化代码、注重效率)和生产阶段打包(压缩代码、优化代码、打包后上线直接使用

我们抽离为三个配置文件

webpack.base.js:所有基础、公共的配置放在该配置文件中
webpack.prod.js:生产环境的单独配置放在该配置文件中放在该配置文件中
webpack.dev.js:开发环境的单独配置放在该配置文件中放在该配置文件中

操作流程

  • 将开发环境和生产环境的公共配置(入口、出口、loader等)放到webpack.base.js

    // webpack.base.js   const path = require("path") const HtmlPlugin = require("html-webpack-plugin") const CleanWebpackPlugin = require("clean-webpack-plugin").CleanWebpackPlugin const CopyWebpackPlugin = require("copy-webpack-plugin") const Webpack = require("webpack") module.exports = {   entry: {     index: "./src/main.js",     other: "./src/other.js",   },   output: {     path: path.resolve(__dirname, "./dist"),     filename: '[name].js'   },   plugins: [     new HtmlPlugin(       {         filename: "index.html",         template: "./src/index.html",         chunks: ["index", "other"]       }     ),     new HtmlPlugin(       {         filename: "other.html",         template: "./src/other.html",         chunks: ["other"]       }     ),     new CleanWebpackPlugin(),     new CopyWebpackPlugin({       patterns: [{         from: path.join(__dirname, "assets"),         to: 'assets'       }]     }),     new Webpack.BannerPlugin("这是一段版权信息"),     new Webpack.ProvidePlugin({       $: "jquery",       jQuery: "jquery"     })   ],   module: {     rules: [       {         test: /\.css$/i,         use: ['style-loader', 'css-loader']       },       {         test: /\.less$/i,         use: ['style-loader', 'css-loader', 'less-loader']       },       {         test: /\.jpg|png|gif|bmp|ttf|eot|svg|woff|wpff2$/i,         type: "asset",         parser: {           dataUrlCondition: {             maxSize: 100           }         },         generator: {           filename: 'img/[name].[hash:6][ext]',           publicPath: './'         }       },      {         test: /\.js$/i,         exclude: /(node_modules|bower_components)/,         use: {           loader: 'babel-loader',           options: {             presets: ['@babel/preset-env'],             plugins: ["@babel/plugin-transform-runtime"]           }         }       },       {         test: /\.(htm|html)$/i,         loader: "html-withimg-loader"       }     ]   } } 复制代码

  • 安装 webpack-merge, 在proddev中,使用webpack.mergebase导出的配置项进行合并导出

    • npm i webpack-merge -D

    • 配置 devprod

      // webpack.dev.js // 1.导入公共webpack配置 const baseConfig = require("./webpack.base.js") // 2.导入合并webpack配置项函数 const merge = require("webpack-merge").merge console.log("merge", merge); // 3.合并配置项 const devConfig = merge(baseConfig, {  mode: "development",  devServer: {     open: true,     port: 3000,     compress: true,    hot: true,    // 服务的基准路径    // contentBase: "./src"  },  devtool: "eval-cheap-module-source-map" }) module.exports = devConfig 复制代码

      // webpack.prod.js // 1.导入公共webpack配置 const baseConfig = require("./webpack.base.js") // 2.导入合并webpack配置项函数 const merge = require("webpack-merge").merge // 3.合并配置项 const prodConfig = merge(baseConfig, {   mode: "production",   // 4.生产环境谨慎使用source-map   // devtool: "cheap-module-source-map" }) module.exports = prodConfig 复制代码

  • 重新配置package.json中,开发和生产环境的脚本指令

    "scripts": {   // 生产环境使用  webpack.prod.js 进行打包   "build": "npx webpack --config webpack.prod.js",   // 开发环境使用  webpack.dev.js 进行打包   "dev": "npx webpack server --config webpack.dev.js", }, 复制代码

配置文件归类

前面根据环境配置了多个配置文件导致根目录文件过多,为了让项目结构清晰方便管理 我们要对配置文件归类处理

  • 将所有Webpack配置文件放到 config 文件夹

    image.png

  • 修改 package.json 中加载配置文件的路径

    "scripts": {   "build": "npx webpack --config ./config/webpack.prod.js",   "dev": "npx webpack server --config ./config/webpack.dev.js" } 复制代码

  • 目录变更,修改配置文件的绝对路径
    注意:配置文件的相对路径都是相对于 项目根路径!!!

    // config/webpack.base.js   module.exports = {   entry: {     // 配置文件中的相对路径都是相对于  项目根目录而非当前文件     index: "./src/main.js",     other: "./src/other.js",   },   output: {     // 1.配置文件中绝对路径是相对于当前文件 所以必须要修改     path: path.resolve(__dirname, "..", "./dist"),     filename: '[name].js'   },   plugins: [    new CopyWebpackPlugin({      patterns: [{        // 2.配置文件中绝对路径是相对于当前文件 所以必须要修改        from: path.join(__dirname, "..", "assets"),        to: 'assets'      }]    }),  ] }  复制代码

定义环境变量

在某些场景下需要定义环境变量,例如根据环境区分接口地址(开发阶段使用内网地址或本机地址)

  • 声明开发阶段、生产阶段的环境变量

    // webpack.dev.js const webpack = require("webpack") const devConfig = merge(baseConfig, {   plugins: [     /** webpack.DefinePlugin(options):声明变量      *  options:配置对象        *    - key:变量名      *    - value:值,表达式      */     new webpack.DefinePlugin({       // 开发环境变量IS_DEV = true       IS_DEV: 'true',       // 变量test = 2       test:'1+1',       // 变量test1 = "1+1"       test1:'"1+1"'     })   ], })    // webpack.prod.js   const webpack = require("webpack") const prodConfig = merge(baseConfig, {   plugins: [     new webpack.DefinePlugin({       // 生产环境变量IS_DEV = false       IS_DEV: 'false',       test: '1+1',       test1: '"1+1"'     })   ], }) 复制代码

  • 根据环境变量动态设置 接口基准地址

    // src/main.js console.log("这是main.js"); // 根据环境设置接口基准地址 let BASEURL = IS_DEV ? "http://127.0.1:3000" : "http://www.baidu.com" console.log("环境变量", BASEURL, IS_DEV, test, test1); 复制代码

  • 开发环境/生产环境打包结果

    image.png

    image.png

通过ajax代理解决跨域问题

由于客户端浏览器的同源策略问题,项目开发阶段访问源之外的接口会出现跨域问题,通过 devSever 配置代理可以解决跨域问题

// webpack.dev.js   const devConfig = merge(baseConfig, {   mode: "development",   devServer: {     open: true,     port: 3000,     compress: true,     hot: true,     proxy: {       // 当发送的ajax请求地址以'/api'开头时,devServer会代理发送一个以'https://www.baidu.com'开头ajax请求       //   /api/index.do  ==>  https://www.baidu.com/idnex.do       '/api': {         // 代理到哪个接口         target: 'https://www.baidu.com',         // 改变接口的源         changeOrigin: true,         ws: true,         // 重写目标接口         pathRewrite: {           '^/api': ''         }       }     }     // 服务的基准路径     // contentBase: "./src"   }, }) 复制代码

模块热更新(HMR)

在开发阶段,devServer会监听代码如果代码更新会重新加载整个页面这会导致几个问题

  • 重新加载页面时丢失的应用程序状态

  • 其他未修改的地方也随着页面刷新重新加载

模块热更新(HMR)不会重新打包整个项目而是以补丁的形式进行局部的更新

  • 修改为单入口文件
    必须为单入口文件才能使用热更新

    // webpack.base.js module.exports = {   entry: {     index: "./src/main.js",     // other: "./src/other.js"   } } 复制代码

  • 新建 src/hotModule.js

    export default "这是热更新模块" 复制代码

  • 监听 hotModule.js

    import str from './hotModule.js' console.log(str); // console.log("这是main.js"); if (module.hot) {   module.hot.accept("./hotModule.js", function () {     const hotModule = require("./hotModule.js")     // 当hotModule 内容更新时触发       console.log("hotModule更新了+++++++++++++++++++++++++++++", hotModule);   }) }  复制代码

  • npm run dev执行脚本  并手动更新src/hotModule.js

    image.png


作者:_光光
链接:https://juejin.cn/post/7017719175380467720


文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐