• webpack5+vue3搭建一个基础的h5项目结构


    一,概述

    本文主要是使用webpack5+vue3+vw适配+axios+vue-router+vuex+vant搭建一个基础的h5项目结构。其中每一节都是独立的配置,想要webpack搭配其他的也可以参考下。至于项目的代码,放在本文最后面,不想看的可以直接下载使用。

    为了自己后续搭建h5项目能有个干净的项目框架直接使用,而不是每次都得重新搭建。每一个步骤都是我实践过的,其中存在的一些注意点和原理,我也会简单提及,算是学习实践的一篇笔记吧。

    二,初始化项目

    第一步:初始化package.json

    npm init -y 
    
    • 1

    第二步:安装webpack

    npm install webpack webpack-cli webpack-dev-server -D
    -D 等价于 --save-dev; 开发环境时所需依赖
    -S 等价于 --save; 生产环境时所需依赖 
    
    • 1
    • 2
    • 3

    第三步:新建src和config文件夹

    新建src文件夹,并写上代码

    ./src/main.js
    
    console.log("hello webpack"); 
    
    • 1
    • 2
    • 3

    新建config文件夹,并新建webpack.config.js文件

    ./config/webpack.config.js
    
    const path = require("path");
    module.exports = {
      entry: path.resolve(__dirname, "../src/main.js"), // 入口文件,打包从这个文件开始
      output: {
        path: path.resolve(__dirname, "../dist"),//出口文件,打包生成的文件放置到这个文件夹下
        filename: "./js/[name].[chunkhash].js"		//打包成的文件名。name取的原始文件名,chunkhash生成哈希值,这样每次打包出来不一样,避免浏览器缓存读取旧文件。
      },
      mode: "development"  //开发模式
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    再在根目录下运行命令行:

    npx webpack --config ./config/webpack.config.js
    //因为我们把webpack配置文件放置到config文件夹内了,所以要用--config来指定配置文件的路径。
    //npx 作用:直接调用项目内部安装的模块,而无需再输入模块路径。npx webpack等价于./node_modules/.bin/webpack --config ./config/webpack.config.js 
    
    • 1
    • 2
    • 3

    于是就可以看到根目录生成了dist文件,完成了基本的打包配置。

    image-20220410113414963

    第四步:在package.json的script中添加命令

    第三步中,我们的运行命令是手动打的,不方便,于是可以在package.json的script中添加命令:

    "scripts": {
        "dev": "webpack --config ./config/webpack.config.js  --progress --color -w"
     },
    //--progress: 启用在构建过程中打印编译进度
    //--color: 启用控制台颜色
    //--watch或-w: 监听文件变化 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    于是在项目根目录执行npm run dev即可完成第三步的打包工作。

    第五步:配置开发服务器

    在第二步的时候,我们安装了这个依赖:webpack-dev-server,它的作用是在本地起一个服务器,当我们运行webpack的时候,打包出来的文件会放置在电脑的内存中(不放dist里了),然后在这个服务器上运行。

    现在在config/webpack.config.js中进行配置这个开发服务器:

    module.exports = {
    	 //...其他配置
      devServer: {
        hot: true, //模块的热替换
        open: true, // 编译结束后自动打开浏览器
        port: 8080, // 设置本地端口号
        host: "localhost" //设置本地url
      }
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后修改package.json文件:

    "dev": "webpack server --config ./config/webpack.config.js  --progress --color", 
    
    • 1

    会报404,因为这时候打开的浏览器,是localhost:8080,它默认打开的是该目录下的index.html文件,目前这样写是没有这个文件的,所以会报404,需要引入一个插件来生成index.html文件。

    第六步:创建 html 文件

    安装依赖:

    npm install html-webpack-plugin -D 
    
    • 1

    然后在根目录新建public文件夹,放置index.html和 favicon.ico文件,htmlWebpackPlugin.options.title是读取html-webpack-plugin插件中配置的值。等运行wenpack打包的时候,就会给它赋值了。

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link rel="icon" href="./favicon.ico" type="image/png" />
        <title><%= htmlWebpackPlugin.options.title %></title>
      </head>
      <body>
        <div id="app"></div>
      </body>
    </html> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    修改 webpack.config.js 配置HtmlWebpackPlugin:

     const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
        // ...其他配置
         plugins: [
            new HtmlWebpackPlugin({
              template: "./public/index.html", //用来做模板的html的文件路径(从项目根目录开始)
              filename: "index.html", //生成的html的名字
              title:'webpack5的项目配置',//这个就对应上文的title
              inject: "body" //打包出来的那个js文件,放置在生成的body标签内
            })
          ],
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这个HtmlWebpackPlugin的作用,就是以你在public/index.html为初始的模板,配置生成html文件。当我们运行npm run dev的时候。就会生成对应的index.html文件,并且自动引入我们打包出来的js文件,这时候再npm run dev就不会报404了。至此,webpack的初始化工作完成。

    image-20220410142259724

    三,分环境打包配置

    在实际开发中,会有开发环境,测试环境生产环境等。而不同环境我们打出来的包应该是不同的,有些配置是开发环境所特有的,而又有一些配置是生产环境所特有的,为了便维护配置文件,我们可以分环境对webpack进行配置。

    第一步,先安装cross-env

    它的作用是设置node环境变量, 因为他可以跨终端进行设置.所以项目中基本上都是使用它。

    npm install cross-env -D 
    
    • 1

    第二步,修改package.json的script

    "dev": "cross-env envMode=dev webpack server --config ./config/webpack.config.js  --progress --color", 
    
    • 1

    主要是加了cross-env envMode=dev这个的作用就是在本地的node全局变量process.env上增加一个名为envMode,值为dev的变量。

    怎么测试是否成功添加呢?可以在webpack.congfig.js中打印一下,然后控制台就会打印出来了,为什么是控制台而不是浏览器?那是因为webpack的程序是在node环境中运行,从而打包出我们想要的bundle.js之类的文件。所以说,是基于node才有了前端工程化。

    image-20220410175528129

    第三步:将需要区分环境的变量导入项目中

    目前,我们只是通过cross-env在node的全局变量中添加了需要的环境变量,而项目代码(src文件夹下的代码),并不能读取这些变量。但区分环境配置不同的参数又是实际开发中必不可少的。比如测试环境和生产环境的接口地址不同,开发环境需要打开Vconsole,而生产环境需要关闭等。

    这就需要我们能够在项目中读取这些变量。就需要另一个库来实现:

    Dotenv 是一个零依赖的模块,它能将环境变量中的变量从 .env 文件加载到 process.env 中。也就是项目代码中能够读取到process.env的值。

    npm i dotenv -D 
    
    • 1

    然后再在webpack.config.js中进行配置:

    const webpack = require("webpack");
    const envMode = process.env.envMode;
    require("dotenv").config({ path: `.env` });//这个的作用是执行根目录下的.env文件,把里面的变量放到process.env上
    require("dotenv").config({ path: `.env.${envMode}` });//这个的作用是执行根目录下的.env.${envMode}文件,把里面的变量放到process.env上,和.env相同的部分会覆盖它
    console.log("-------", envMode);
    // 正则匹配以 VUE_APP_ 开头的 变量
    const prefixRE = /^VUE_APP_/;
    let env = {};
    // 只有 NODE_ENV,BASE_URL 和以 VUE_APP_ 开头的变量将通过 webpack.DefinePlugin 静态地嵌入到客户端侧的代码中
    for (const key in process.env) {
      if (key == "NODE_ENV" || key == "BASE_URL" || prefixRE.test(key)) {
        env[key] = JSON.stringify(process.env[key]);
      }
    }
    module.exports = {
     ...//
      plugins: [
        new webpack.DefinePlugin({
          // 定义环境和变量
          "process.env": {
            ...env
          },
           __VUE_OPTIONS_API__: false,//避免控制台警告信息
          __VUE_PROD_DEVTOOLS__: false
        }),
        ...//
      ]
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    注意到,它是需要手动获取到变量的,主要就是这两行代码:

    require("dotenv").config({ path: `.env` });//这个的作用是执行根目录下的.env文件,把里面的变量放到process.env上
    require("dotenv").config({ path: `.env.${envMode}` });//这个的作用是执行根目录下的.env.${envMode}文件,把里面的变量放到process.env上,和.env相同的部分会覆盖它 
    
    • 1
    • 2

    这样配置之后,就可以和vue-cli创建的项目一样,在根目录创建环境变量并使用了,可以参考

    第四步:根目录创建.env等文件

    .env

    # 公共环境配置
    VUE_APP_TITLE = "webpack5+vue3" 
    
    • 1
    • 2

    .env.dev

    NODE_ENV = 'development'
    #开发环境要代理,这个地方要写
    VUE_APP_baseUrl = '/api'
    VUE_APP_SHOWCONSOLE=true 
    
    • 1
    • 2
    • 3
    • 4

    .env.testing

    NODE_ENV = 'testing'
    VUE_APP_baseUrl = '../../'
    VUE_APP_SHOWCONSOLE = true 
    
    • 1
    • 2
    • 3

    .env.prod

    NODE_ENV = 'production'
    VUE_APP_baseUrl = '../../'
    VUE_APP_SHOWCONSOLE = false 
    
    • 1
    • 2
    • 3

    这样配置之后,当我们运行npm run dev的时候,就会把.env.env.dev文件中的环境变量放置到项目的全局变量process.env中,我们可以在项目中把它打印出来:

    image-20220410183940808

    同样的,如果我们在package.json中运行的脚本是envMode=test的话,这里获取到的变量就会是.env.env.testing两个文件的并集了。项目代码中就能够使用这些变量来处理不同环境中的逻辑了。

    第五步:区分开发环境和生产环境的webpack配置

    第三第四步主要是为了项目代码中能够使用环境变量,当我们执行完第二步,webpack能根据我们的命令行区分开发和生产环境时,便可以执行区分开发和生产的配置了。

    从上文可以知道。webpack配置文件最终是导出一个对象给webpack使用,而我们实际开发中,开发环境是有自己特有的配置,生产环境有自己特有的配置,还有一部分公用的配置。

    于是就可以把开发的配置抽离出来,生产的配置抽离出来,公用的配置抽离出来。如下图所示:

    image-20220410190317640

    这就有个问题,某个环境的配置如何和公用配置合并呢?理想的情况是取并集,并且是对象深层的并集。

    有一个插件提供了这个功能,现在安装下这个插件:

    npm i webpack-merge -D 
    
    • 1

    然后新建三个文件,把我们之前做的配置按照开发环境、生产环境和公用配置进行拆分,并删除之前的配置文件:

    webpack.base.conf.js

    const path = require("path");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const webpack = require("webpack");
    const envMode = process.env.envMode;
    require("dotenv").config({ path: `.env` });
    require("dotenv").config({ path: `.env.${envMode}` });
    console.log("-------", envMode);
    // 正则匹配以 VUE_APP_ 开头的 变量
    const prefixRE = /^VUE_APP_/;
    let env = {};
    // 只有 NODE_ENV,BASE_URL 和以 VUE_APP_ 开头的变量将通过 webpack.DefinePlugin 静态地嵌入到客户端侧的代码中
    for (const key in process.env) {
      if (key == "NODE_ENV" || key == "BASE_URL" || prefixRE.test(key)) {
        env[key] = JSON.stringify(process.env[key]);
      }
    }
    
    module.exports = {
      entry: path.resolve(__dirname, "../src/main.js"), // 入口
      plugins: [
        new webpack.DefinePlugin({
          // 定义环境和变量
          "process.env": {
            ...env
          },
           __VUE_OPTIONS_API__: false,//这两个配置是引入vue后,避免控制台警告信息的
          __VUE_PROD_DEVTOOLS__: false
        }),
        new HtmlWebpackPlugin({
          template: "./public/index.html", //用来做模板的html的文件路径
          filename: "index.html", //生成的html的名字
          title: "webpack5的项目配置", //这个就对应上文的title
          inject: "body" //打包出来的那个js文件,放置在生成的body标签内
        })
      ]
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    webpack.dev.conf.js

    const path = require("path");
    const { merge } = require("webpack-merge");
    const BaseWebpackConfig = require("./webpack.base.conf");
    module.exports = merge(BaseWebpackConfig, {
      mode: "development",
      output: {
        path: path.resolve(__dirname, "../dist"),
        filename: "./js/[name].[chunkhash].js"
      },
      devServer: {
        hot: true, //模块的热替换
        open: true, // 编译结束后自动打开浏览器
        port: 8080, // 设置本地端口号
        host: "localhost" //设置本地url
      }
    }); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    webpack.prod.conf.js

    const path = require("path");
    const { merge } = require("webpack-merge");
    const BaseWebpackConfig = require("./webpack.base.conf");
    module.exports = merge(BaseWebpackConfig, {
      mode: "production",
      output: {
        path: path.resolve(__dirname, "../dist"),
        filename: "./js/[name].[chunkhash].js"
      }
    }); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    然后修改packgae.json中的script为:

     "scripts": {
        "dev": "cross-env envMode=dev webpack serve --config ./config/webpack.dev.conf.js  --color ",
        "testing": "cross-env envMode=testing webpack --config ./config/webpack.prod.conf.js  --color",
        "build": "cross-env envMode=prod webpack --config ./config/webpack.prod.conf.js  --color"
      }, 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    然后运行npm run dev,就可以执行./config/webpack.dev.conf.js中的打包代码配置了,达到的效果和之前一样:

    image-20220410193209897

    四,配置babel,es6转es5

    有些浏览器或者浏览器版本还不支持es6的语法,所以需要babel把es6转化为es5。

    第一步:安装babel相关的依赖

    npm install  babel-loader @babel/core @babel/preset-env -D 
    
    • 1

    第二步:安装处理async和await的依赖

    npm install  @babel/runtime @babel/plugin-transform-runtime -D 
    
    • 1

    第三步:配置webpack

    因为这属于生产环境和开发环境都需要用到的配置,所以在webpack.base.js中进行配置。

    module.exports = {
    	...//
      module: {
        rules: [
          {
            test: /\.js$/,
            use: {
              loader: "babel-loader",
              options: {
                presets: ["@babel/preset-env"],
                plugins: [["@babel/plugin-transform-runtime"]],
                //开启缓存
                cacheDirectory: true
              }
            },
            exclude: /node_modules/
          }
        ]
      }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    值得注意的是:若不想将配置写在配置文件中,可在项目根目录创建 babel.config.jsbabelrc.js 文件(优先级更高)。

    这样配置之后,遇到js文件,就会使用babel进行编译处理。

    五,引入vue框架

    第一步,安装识别解析vue文件的依赖

    npm i vue-loader @vue/compiler-sfc -D
    npm i vue -S 
    
    • 1
    • 2

    第二步,创建vue文件,并且在入口文件创建vue实例

    入口文件main.js

    import { createApp } from "vue";
    import App from "./App.vue";
    const app = createApp(App);
    app.mount("#app"); 
    
    • 1
    • 2
    • 3
    • 4

    App.vue文件

    <template>
      <div class="app-box">测试页面</div>
    </template>
    <script setup></script>
    <style></style> 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    第三步,配置webpack可以识别vue文件

    同样是通用配置,在webpack.base.js中配置:

    const { VueLoaderPlugin } = require("vue-loader/dist/index");
    module.exports = {
    	...//其他配置
      module: {
        rules: [
         ...//其他配置
          {
            test: /\.vue$/,
            loader: "vue-loader"
          }
        ]
      },
      plugins: [
       ...//其他配置
        new VueLoaderPlugin()
      ]
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    配置完成后可以在浏览器上看到:

    image-20220410234408018

    六,解析scss文件

    第一步,安装依赖

    npm install css-loader sass sass-loader mini-css-extract-plugin -D 
    
    • 1

    第二步,配置webpack

    还是在通用文件中进行配置:

    //1,引入
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    //2,plugins中使用
     plugins: [
        new MiniCssExtractPlugin({
          filename: "assets/styles/[contenthash].css" //配置css打包之后的存放路径(这个配置contenthash在开发环境会导致热更新css报错,开发环境用name)
        })
      ]
     //3,module.rule中配置规则
     	{
            test: /\.(scss|css)$/,
            use: [
              {
                loader: MiniCssExtractPlugin.loader
              },
              "css-loader",
              "sass-loader"
            ]
          } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    值得注意的是以前我们都是使用style-loader将css挂在dom上,现在的做法是使用MiniCssExtractPlugin插件将css抽离出来成为一个文件,然后再在html中使用link来引入了。

    另外,这个use中的数组是有顺序要求的。先执行sass-loader转化为一般的css文件,再执行css-loader转化为webpack能够理解的模块进行打包,最后使用MiniCssExtractPlugin在html文件中引入。

    然后运行项目,可以看到已经能识别scss了。

    image-20220411000855383

    第三步,使用postcss插件给css添加兼容性前缀

    一些css3的代码,一些浏览器不支持。需要添加浏览器前缀。就需要使用到这个插件。

    npm i postcss-loader autoprefixer -D 
    
    • 1

    然后修改刚刚设置的css配置,在scss-loader后面增加这个postcss-loader,也就是转化成普通的css后需要增加浏览器前缀。

    {
           test: /\.(scss|css)$/,
           use: [
             {
                loader: MiniCssExtractPlugin.loader
             },
              "css-loader",
              "postcss-loader",
              "sass-loader"
           ]
     } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    但是增加前缀是需要使用postcss-loader的一个插件。

    于是需要再在项目根目录新建一个文件:postcss.config.js文件

    module.exports = () => {
      return {
        plugins: {
          autoprefixer: {}
        }
      };
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里需要配置浏览器的兼容列表,但是更好的做法是在package.json文件中进行配置:

    "browserslist": [
        "> 0.1%",
        "last 2 versions"
      ], 
    
    • 1
    • 2
    • 3
    • 4

    于是运行项目就可以看到增加了前缀了:

    image-20220411002220063

    七,处理图片这类静态资源

    我们在代码中直接写:

    <div class="app-box">
        <img src="./assets/img/pic.jpeg" alt="" />
      </div> 
    
    • 1
    • 2
    • 3

    是会报错的,因为这时候webpack还不能解析图片等静态资源。

    webpack5 之前我们处理静态资源比如。图片字体之类的资源的时候等,需要用到url-loader,file-loader,raw-loader,webpack5则放弃了这三个loader,这三个loader在github上也停止了更新。 webpack5使用四种新增的内置资源模块(Asset Modules)替代了这些loader的功能。

    asset/resource 将资源分割为单独的文件,并导出url,就是之前的 file-loader的功能.
    asset/inline 将资源导出为dataURL(url(data:))的形式,之前的 url-loader的功能.
    asset/source 将资源导出为源码(source code). 之前的 raw-loader 功能.
    asset 自动选择导出为单独文件或者 dataURL形式(默认为8KB). 之前有url-loader设置asset size limit 限制实现。 
    
    • 1
    • 2
    • 3
    • 4

    第一步,全局设置默认的文件输出地址

    在公共配置里设置assetModule的文件输出路径:

    output: {
        assetModuleFilename: "assets/images/[contenthash][ext]" //自定义asset module资源打包后的路径和名字
      }, 
    
    • 1
    • 2
    • 3

    第二步,在modele.rules中进行配置

    因为asset类型可以设置文件大小,所以对于图片的处理我们通常使用它。(大于多少kb的使用单独文件的url,小图片则转为dataurl,即base64的形式。)

    {
             test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 
             type: 'asset', // asset 资源类型可以根据指定的图片大小来判断是否需要将图片转化为 base64
              generator: {
                filename: 'assets/images/[hash][ext][query]' // 局部指定输出位置,这里配置的文件输出路径优先级比第一步配置的高
              },
              parser: {
                dataUrlCondition: {
                  maxSize: 30 * 1024 // 限制于 30kb
              }
       } 
    }, 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    值得注意的是,这里配置的文件输出路径优先级更高。

    第三步,分别引入大图片和小图片作为测试

    如下图,可以看到大图片是打包成文件了,然后依据路径引入,而小图片,则是直接编译成base64:

    image-20220411221246799

    八,配置 alias 路径别名

    在我们引入文件或者资源的时候,常常使用相对路径的话,要是文件路径特别深的话。就得写好多好长的路径,这样子显然不方便。于是就可以配置路径别名的方式来简化这一过程。

    还是在公用配置文件中写:

    function resolve(dir) {
      return path.join(__dirname, '..', dir)
    }
    module.exports = {
      ...//其他配置
      resolve: {
        alias: {
          "@": resolve("src"),
          "@components": resolve("src/components"),
          "@assets": resolve("src/assets"),
          "@img": resolve("src/assets/img"),
          "@utils": resolve("src/utils"),
          "@api": resolve("src/api"),
          "@css": resolve("src/assets/css"),
          "@plugins": resolve("src/plugins")
        }
      }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这样配置之后。如上文我们使用图片就可以:

    <template>
      <div class="app-box">
        <div class="img1-box">
          <h4>这个是大图片</h4>
          <img src="@img/pic.jpeg" alt="" />
        </div>
        <div class="img2-box">
          <h4>这个是小图片</h4>
          <img src="@img/test.png" alt="" />
        </div>
      </div>
    </template> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    九,打包时清除上次构建dist目录

    webpack5.20以下版本清除dist文件内容一般使用插件 clean-webpack-plugin, 5.20版本以后output新增特性clean,用于清除dist文件。

    因为只有打包出来才会生成dist文件,所以我们把这个配置放在生产的配置文件中:

    //webpack.prod.conf.js
    module.exports = {
      //...
      output: {
        clean: true, // Clean the output directory before emit.
      },
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    十,直接复制指定文件到dist下指定路径

    一个完整的项目,不仅有开发依赖的各种文件需要打包上传到服务器上,也许还会有各种开发文档,项目中没引用过的图片、设计图、或者会有一些静态页面不需要编译,也要上传到服务器上,以方便预览。

    这里就需要用到插件copy-webpack-plugin

    第一步:安装copy-webpack-plugin

    npm i copy-webpack-plugin -D 
    
    • 1

    第二步:配置将public中favicon.ico文件复制到dist根目录

    因为之前我的html模板中引入favicon.ico是写死了相对路径,npm run build生成的dist下不会有这个文件,所以会报404。为了解决这一问题,可以正好用这个插件把public文件夹下的favicon.ico复制到dist根目录下:

    <link rel="icon" href="./favicon.ico" type="image/png" /> 
    
    • 1

    因为这是打包dist文件时才需要执行,所以写在生产的配置文件中:

    const copyWebpackPlugin = require("copy-webpack-plugin");
    function resolve(dir) {
      return path.join(__dirname, "..", dir);
    }
    module.exports = merge(BaseWebpackConfig, {
    	...//其他配置
     plugins: [
        //静态资源输出到根目录
        new copyWebpackPlugin({
          patterns: [
            {
              from: resolve("/public"),
              to: resolve("/dist"), //放到output文件夹下
              globOptions: {
                dot: true,
                gitignore: false,
                ignore: [
                  // 配置不用copy的文件
                  "**/index.html"
                ]
              }
            }
          ]
        })
      ]
     } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    于是运行npm run build,就可以看到dist根目录下会出现favicon.ico文件了。

    image-20220411230130519

    十一,配置引入字体文件

    和第十步一样,使用webpack的asset module内置模块。

     {
            test: /\.(eot|svg|ttf|woff|woff2|)$/,
            type: 'asset/resource',
            generator: {
              // 输出文件位置以及文件名
              filename: 'assets/fonts/[hash:8].[name][ext]'
            }
    }, 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    十二,配置压缩

    1,压缩css

    之前上文第六步,已经安装了抽离css为单独文件,然后link便签引入html的插件。现在来安装压缩css的插件。

    第一步:安装插件
    npm install css-minimizer-webpack-plugin -D 
    
    • 1
    第二步:配置
    const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
    
    module.exports = {
     ...//其他配置
      optimization: {
        minimizer: [
          new CssMinimizerPlugin(),
        ],
      },
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    查看配置之后的对比:

    image-20220417111618216

    2,压缩 html 文件

    在上文的第二节的第六步,已经安装并配置了html-webpack-plugin,这里就不再次安装,而是直接在生产配置文件webpack.prod.conf.js中增加配置,因为之前的文件是写在webpack.base.conf.js中,这里再写,当运行npm run build的时候,就会有冲突(base中配置了,prod中又配置),所以呢,就需要把第二节的第六步中的配置转移到开发环境配置中去:

    于是公用配置:

    //webpack.base.conf.js
    -  const HtmlWebpackPlugin = require("html-webpack-plugin");
    
       module.exports = {
        plugins: [
    	...//其他配置
    -        new HtmlWebpackPlugin({
    -          template: "./public/index.html", //用来做模板的html的文件路径
    -          filename: "index.html", //生成的html的名字
    -          title: "webpack5的项目配置", //这个就对应上文的title
    -          inject: "body" //打包出来的那个js文件,放置在生成的body标签内
    -        }),
    
          ]
        }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    开发环境配置:

    //webpack.base.conf.js
    +  const HtmlWebpackPlugin = require("html-webpack-plugin");
    
       module.exports = {
        plugins: [
    	...//其他配置
    +        new HtmlWebpackPlugin({
    +          template: "./public/index.html", //用来做模板的html的文件路径
    +          filename: "index.html", //生成的html的名字
    +          title: "webpack5的项目配置", //这个就对应上文的title
    +          inject: "body" //打包出来的那个js文件,放置在生成的body标签内
    +        }),
    
          ]
        }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    生产环境配置:

    //webpack.prod.conf.js
    
    +  const HtmlWebpackPlugin = require('html-webpack-plugin');
    module.exports = {
        // ...
        plugins: [
    +        new HtmlWebpackPlugin({
    +          template: "./public/index.html", //用来做模板的html的文件路径
    +          filename: "index.html", //生成的html的名字
    +          title: "webpack5的项目配置", //这个就对应上文的title
    +          inject: "body", //打包出来的那个js文件,放置在生成的body标签内
    +          minify: {
    +            collapseWhitespace: true, // 去掉空格
    +            removeComments: true // 去掉注释
    +          }
    +        })
            // ...
        ]
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    对比开发环境打包出来的html和生产的:

    image-20220417121326621

    3,压缩js文件

    第一步:安装依赖
    npm install terser-webpack-plugin -D 
    
    • 1
    第二步:配置依赖
    const TerserWebpackPlugin = require('terser-webpack-plugin');
    
    module.exports = {
        // ...
        optimization: {
            minimize: true,
            minimizer: [
                new TerserWebpackPlugin()
            ]
        },
        // ...
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    第三步:配置测试环境和生产环境的js压缩

    在实际开发过程中,测试环境的配置和生产基本一样,所以共用prod文件进行配置。但是还是有一丢丢区别。比如在压缩js的时候。生产环境我们希望去除console,而测试环境又希望保留。于是就需要区分配置。这个前提是要让webpack区分测试环境和生产环境。

    在上文,第三节的第五步,已经区分环境打包了。并且注入了环境变量。

    image-20220417135434601

    于是,当我们运行npm run testing的时候,process.env.NODE_ENV=testing。而当我们运行npm run build的时候,process.env.NODE_ENV=production

    于是在webpack.prod.conf.js中就可以这样配置js的压缩。

    const TerserWebpackPlugin = require('terser-webpack-plugin');
    //只有生产的包会清除log
    const consoleObj = function () {
      if (process.env.NODE_ENV === "production") {
        return {
          // 多进程
          parallel: true,
          //删除注释
          extractComments: false,
          terserOptions: {
            compress: {
              // 生产环境去除console
              drop_console: true,
              drop_debugger: true
            }
          }
        };
      } else {
        return {
          // 多进程
          parallel: true
        };
      }
    };
    
    module.exports = {
        // ...
        optimization: {
            minimize: true,
            minimizer: [
                new TerserWebpackPlugin(consoleObj())
            ]
        },
        // ...
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    但是我执行npm run testing的时候,遇到了一个bug。报错了:

    WARNING in DefinePlugin
    Conflicting values for 'process.env.NODE_ENV' 
    
    • 1
    • 2

    意思就是process.env.NODE_ENV已经存在,所以冲突了,查看了官网:

    Tells webpack to set process.env.NODE_ENV to a given string value. optimization.nodeEnv uses DefinePlugin unless set to false. optimization.nodeEnv defaults to mode if set, else falls back to 'production'. 
    
    • 1

    是因为optimization.nodeEnv,如果不设置值的话,默认会取mode中设置的值,而恰好我mode的设置:开发环境是development,生产环境是production。和这两个文件的配置一样,所以不会冲突:

    image-20220417140952871

    而这时候,我运行npm run testing的时候,因为执行的还是生产配置的文件,其中的mode已经设置为production了。于是会报这个冲突的错误。

    解决办法是在webpack.base.conf.js中添加这一行:

    module.exports={
    	...//其他配置
    	optimization: {
        	nodeEnv: false
      	}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这样配置之后,process.env.NODE_ENV就不会从配置文件的mode取值了,而是直接用我们自己配置的webpack.DefinePlugin:

    image-20220417142041733

    第四步:对比配置前后的js文件是否压缩

    image-20220417143835050

    十三,打包友好提示

    到目前为止,我们一运行打包,控制台会输出这么多东西:

    image-20220417151541770

    为了简化,我们在webpack.base.conf.js加入stats: "errors-only",这样只有在报错的时候会输出。

    image-20220417151807190

    为了更好的查看报错的信息,我们可以继续安装插件:

    第一步:安装依赖

    npm install friendly-errors-webpack-plugin  -D 
    
    • 1

    第二步:进行配置

    plugins: [
        
        new FriendlyErrorsWebpackPlugin({
          // 成功的时候输出
          compilationSuccessInfo: {
            messages: [`已经打包成功啦~`]
          },
          // 是否每次都清空控制台
          clearConsole: true
        })
      ], 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    十四,分析打包文件大小

    有的时候,我们需要对项目进行优化。比如说减少首屏加载时间等,就需要直观地查看每个文件打包出来的大小。于是就需要这么一个插件。来查看打包文件的大小。

    第一步:安装依赖

    npm install webpack-bundle-analyzer -D 
    
    • 1

    第二步:修改 webpack.prod.conf.js 文件

     const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
    module.exports = {
      // ...
      plugins: [
           new BundleAnalyzerPlugin({
          analyzerMode: process.env.analyMode == "true" ? "server" : "disabled", //这个配置后默认是不会启用这个插件
          generateStatsFile: false, // 是否生成stats.json文件
          statsOptions: { source: false }
        })
        // ...
      ],
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    第三步:修改 package.json 文件

    因为查看生产配置才有意义,所以就写在生产配置中了:

    {
        "scripts": {
         // ...
         "analyzer": "cross-env envMode=prod analyMode=true webpack --config ./config/webpack.prod.conf.js  --color --progress"
      },
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    控制台执行 npm run analyzer 系统自动启动打包报告的HTTP服务器:

    image-20220417160233894

    它会帮助我们统计出每个文件的三种大小:

    stat size(统计尺寸)
    parsed size(解析大小,webpack打包出来的文件大小)
    Gzipped size(压缩大小,在开启了gzip后达到的大小) 
    
    • 1
    • 2
    • 3

    十五,引入vue-router

    第一步:安装依赖

    npm install vue-router -S 
    
    • 1

    第二步:编写相关的代码

    //home页面
    <template>
      <div class="home-box">
        <div class="home-title">首页页面</div>
        <button class="home-btn" @click="goToMine">前往我的页面</button>
      </div>
    </template>
    <script setup>
    import { useRouter } from "vue-router";
    const router = useRouter();
    function goToMine() {
      router.push("/mine");
    }
    </script>
    <style></style> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    //mine页面
    <template>
      <div class="mine-box">
        <div class="mine-title">我的页面</div>
        <button class="mine-btn" @click="goToHome">前往首页</button>
      </div>
    </template>
    <script setup>
    import { useRouter } from "vue-router";
    const router = useRouter();
    function goToHome() {
      router.push("/home");
    }
    </script>
    <style></style> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    //router.js
    import { createRouter, createWebHashHistory } from "vue-router";
    import Home from "@/views/home/index.vue";
    import Mine from "@/views/mine/index.vue";
    const routerHistory = createWebHashHistory();
    const router = createRouter({
      history: routerHistory,
      routes: [
        {
          path: "/",
          redirect: "/home",
          component: Home,
          children: [
            {
              path: "/home",
              name: "home",
              component: Home,
              meta: {
                keepAlive: false
              }
            }
          ]
        },
        {
          path: "/mine",
          name: "Mine",
          component: Mine,
          meta: {
            keepAlive: false
          }
        }
      ]
    });
    export default router; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    然后App.vue

    //App.vue
    <template>
      <div id="app">
        <router-view v-if="!$route.meta.keepAlive" class="router" />
        <router-view
          v-if="$route.meta.keepAlive"
          v-slot="{ Component }"
          class="router"
        >
          <keep-alive>
            <component :is="Component" />
          </keep-alive>
        </router-view>
      </div>
    </template>
    <script setup></script>
    <style lang="scss"></style> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    最后main.js:

    //main.js
    import { createApp } from "vue";
    import App from "./App.vue";
    import router from "./router/index.js";
    const app = createApp(App);
    app.use(router).mount("#app"); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    运行项目,就可以看到:

    image-20220417202508411

    十六,引入vuex

    虽然vue3的compositionAPI可以替代vuex。但我还是比较倾向于用vuex。感觉用vuex模块化管理页面的状态数据,然后compositionAPI用来封装抽离通用的功能性代码。

    第一步:安装vuex

    npm install vuex -S 
    
    • 1

    第二步:模块化配置vuex

    src/store/modules/user.js

    const state = {
      userInfo: {}
    };
    
    const mutations = {
      setUserInfo(state, infoObj) {
        state.userInfo = infoObj;
      }
    };
    const actions = {
      async getUserInfo({ commit }) {
        //异步接口请求
        setTimeout(() => {
          let obj = {
            userName: "姓名",
            age: 18
          };
          commit("setUserInfo", obj);
        }, 0);
      }
    };
    
    export default {
      namespaced: true,
      state,
      mutations,
      actions
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    src/store/modules/index.js

    import { createStore } from "vuex";
    import user from "./modules/user";
    const store = createStore({
      state: {},
      getters: {},
      actions: {},
      mutations: {},
      modules: {
        user
      }
    });
    
    export default store; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    然后在main.js中:

    import { createApp } from "vue";
    import App from "./App.vue";
    import router from "./router/index.js";
    import store from "./store/index.js";
    const app = createApp(App);
    app
    .use(router)
    +     .use(store)
    .mount("#app"); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    最后在home页面使用:

    <template>
      <div class="home-box">
        <div class="home-title">首页页面</div>
        <div class="home-content">
          <div class="content-title">测试store中的数据</div>
          <div class="user-name">{{ userObj.userName }}</div>
        </div>
    
        <button class="home-btn" @click="goToMine">前往我的页面</button>
      </div>
    </template>
    <script setup>
    import { computed } from "vue";
    import { useRouter } from "vue-router";
    import { useStore } from "vuex";
    const router = useRouter();
    const store = useStore();
    const userObj = computed(() => store.state.user.userInfo);
    store.dispatch("user/getUserInfo");
    
    function goToMine() {
      router.push("/mine");
    }
    </script>
    <style></style> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    于是就可以看到页面效果:

    image-20220417204659388

    十七,引入vant

    第一步:安装依赖

    npm i vant -S 
    
    • 1

    第二步:按需引入vant

    需要再安装一个依赖

    npm i babel-plugin-import -D 
    
    • 1

    第三步:新建babel.config.js

    const presets = [];
    const plugins = [
      //配置vant的按需引入,babel-plugin-import 是一款 babel 插件
      [
        'import',
        {
          libraryName: 'vant',
          libraryDirectory: 'es',
          style: true
        },
        'vant'
      ]
    ];
    module.exports = {
      plugins,
      presets
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    第四步:新建plugin文件夹按需引入vant组件

    import { Button } from "vant";
    export default (app) => {
      app.use(Button);
    }; 
    
    • 1
    • 2
    • 3
    • 4

    第五步:在main中注册

    import { createApp } from "vue";
    import App from "./App.vue";
    import router from "./router/index.js";
    import store from "./store/index.js";
    +   import installVant from "@/plugins/vant.js";
    const app = createApp(App);
    +    installVant(app);
    app.use(router).use(store).mount("#app"); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    第六步:在页面中使用

    image-20220418002512463

    十八,配置vw自适应

    第一步:安装依赖

    npm i postcss-px-to-viewport -D 
    
    • 1

    第二步:添加根目录下postcss.config.js配置

    module.exports = ({ file }) => {
      const designWidth = file.includes(path.join("node_modules", "vant"))
        ? 375
        : 750;//这一步是因为vant的基准是375
      return {
        plugins: {
          autoprefixer: {},
          "postcss-px-to-viewport": {
            unitToConvert: "px", // 需要转换的单位,默认为"px"
            viewportWidth: designWidth, //  设计稿的视口宽度
            unitPrecision: 5, // 单位转换后保留的精度
            propList: ["*"], // 能转化为vw的属性列表
            viewportUnit: "vw", //  希望使用的视口单位
            fontViewportUnit: "vw", // 字体使用的视口单位
            selectorBlackList: [".ignore", ".hairlines", ".ig-"], // 需要忽略的CSS选择器
            minPixelValue: 1, // 最小的转换数值,如果为1的话,只有大于1的值会被转换
            mediaQuery: false, // 媒体查询里的单位是否需要转换单位
            replace: true, // 是否直接更换属性值,而不添加备用属性
            include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换,例如只转换 'src/mobile' 下的文件 (include: /\/src\/mobile\//)
            landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
            landscapeUnit: "vw", // 横屏时使用的单位
            landscapeWidth: 568 // 横屏时使用的视口宽度
          }
        }
      };
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    第三步:查看效果

    image-20220423152521328

    十九,引入axios

    第一步:安装axios依赖

    npm i axios -S 
    
    • 1

    第二步:新建api文件夹,下新建request.js,封装axios

    import axios from "axios";
    import { Toast } from "vant";
    import qs from "qs";
    axios.defaults.headers["Content-Type"] = "application/json; charset=UTF-8";
    
    export default function request(options) {
      return new Promise((resolve, reject) => {
        // 创建axios实例
        const service = axios.create({
          // axios中请求配置有baseURL选项,表示请求URL公共部分
          baseURL: process.env.VUE_APP_baseUrl, //baseURL 将会被加在 url 前面。
          // 超时
          timeout: 10000,
          withCredentials: true // 是否允许带cookie这些
        });
        // request拦截器
        service.interceptors.request.use(
          async (config) => {
            switch (config.method) {
              case "get":
                config.params = config.params;
                break;
              case "post":
                config.headers["Content-Type"] =
                  "application/x-www-form-urlencoded; charset=UTF-8";
                config.data = qs.stringify(config.data);
                break;
              default:
            }
            return config;
          },
          (error) => {
            Toast.clear();
            console.log(error);
            Promise.reject(error);
          } //请求拦截器的报错处理
        );
    
        // 响应拦截器
        service.interceptors.response.use(
          (res) => {
            const handleErrCode = res.data.code;
            if (handleErrCode === "10000") {
              resolve(res.data);
            } else {
              console.log("其他状态码的处理逻辑");
              reject(res.data);
            }
          },
          (error) => {
            Toast.clear();
            console.log("err" + error);
            let { message } = error;
            if (message == "Network Error") {
              message = "网络异常,请检查网络";
            } else if (message.includes("timeout")) {
              message = "系统接口请求超时,请检查网络";
            } else if (message.includes("Request failed with status code")) {
              message = "系统接口" + message.substr(message.length - 3) + "异常";
            }
            Toast({
              message: message,
              type: "error",
              duration: 5 * 1000
            });
            return reject(error);
          }
        );
        service(options);
      });
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    第三步:引出get和post请求

    api文件夹下新建http.js

    import request from "@/api/request";
    export function httpPost(data, url) {
      return request({
        url: url,
        method: "post",
        data
      });
    }
    export function httpGet(data, url) {
      return request({
        url: url,
        method: "get",
        data
      });
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    第四步:开发服务器的本地代理

    注意到第二步的axios封装中,有这一行配置:

    baseURL: process.env.VUE_APP_baseUrl, //baseURL 将会被加在 url 前面。 
    
    • 1

    process.env.VUE_APP_baseUrl已经在上文第三节第四步,在环境变量配置文件中新建并注入项目中了:

    image-20220423181901700

    接下来,在webpack.dev.conf.js中配置开发服务器代理。因为我们在本地开发环境写代码的时候。请求的后端接口是跨域的,为了解决这个跨域问题,就可以利用devServer给我们提供的一个本服务器代理的方式。

    原理大概如下图这般:

    image-20220423183433443

    因为浏览器有同源策略,直接访问服务端服务器会跨域,而服务器之间不会跨域。所以就在本地建立了一个代理来转发请求。

    现在来配置这个代理服务器:

    module.exports = merge(BaseWebpackConfig, {
      //..其他配置
      devServer: {
        hot: true, //模块的热替换
        open: true, // 编译结束后自动打开浏览器
        port: 8080, // 设置本地端口号
        host: "localhost", //设置本地url
        // 设置代理,用来解决本地开发跨域问题
        proxy: {
          "/api": {
            secure: false,
            changeOrigin: true,
            target:
              "https://www.fastmock.site/mock/88bbb3bb8d6ea3dc8f09431a61ce2e50/mymock_test",
            pathRewrite: { "^/api": "" }
          }
        }
      },
     } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    开发环境下,我们已经配置了 process.env.VUE_APP_baseUrl=='/api'

    第五步,发送请求

     <div class="five item">
            <div class="content-title">测试axios请求数据</div>
            <van-button type="success" class="home-btn" @click="testAxios">
              发送请求
            </van-button>
            <div class="content-request">{{ renderTest }}</div>
          </div> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    import { httpGet } from "@/api/http.js";
        //发送请求
     async function testAxios() {
          try {
            let params = {};
            const res = await httpGet(params, "/justtest");
            renderTest.value = res.data.content;
          } catch (error) {}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    页面效果:

    222

    二十,引入Vconsole

    有的时候,我们的h5是需要内嵌在app上的,为了能够查看报错,可以安装一下Vconsole插件:

    第一步:安装依赖

    npm i vconsole -S 
    
    • 1

    第二步,项目中配置

    在上文的第三节第四步,我们已经分环境配置好变量并注入项目中了。于是就可以在main.js中配置:

    image-20220423192239064

    //main.js
    
    // 开发测试环境显示console
    if (process.env.VUE_APP_SHOWCONSOLE === "true") {
      let Vconsole = require("../node_modules/vconsole/dist/vconsole.min");
      new Vconsole();
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    第三步:查看效果:

    image-20220423192347758

    二十一,代码地址及小结

    项目地址(本文内容在master分支上):

    https://gitee.com/ling-xu/webapck5 
    
    • 1

    小结:

    第一次在掘金写博客,这篇文章算是开个头,后续再加上eslint配置这些。慢慢更新吧。

    关于我:

    一个毕业后在工厂拧了两年螺丝的前大国工酱,现前端新手村成员。写博客主要是为了分享和记录,内容比较简单,还望路过的大佬斧正。 
    
    • 1
  • 相关阅读:
    POJ1094Sorting It All Out题解
    windows环境下安装logstash同步数据,注册系统服务
    【01】Spring源码-手写篇-手写IoC实现
    使用Spring Boot 记录 MongoDB查询 日志(Log)
    【Java从入门到精通 08】:面向对象编程(进阶部分)
    散户反着买,别墅靠大海?股票上了龙虎榜还能买吗?
    Spring Security基本框架之认证和授权
    IE落幕,微软IE浏览器永久关闭
    Vue2.0+AntvX6—边 Edge
    Linux多线程【线程互斥与同步】
  • 原文地址:https://blog.csdn.net/web22050702/article/details/125541317