zoukankan      html  css  js  c++  java
  • 第四节:Webpack本地服务器搭建、剖析devServer的HRM热替换和其它配置、resolve模块解析、区分开发/生产环境方案实战

    一. webpack本地服务器搭建

    1. 为什么要搭建本地服务器?

     我们之前通过npm run build】,编译相关的代码; 然后需要在浏览器中运行,每次修改修改代码,都需要重新编译运行,很麻烦。我们希望可以做到,文件发生变化时,可以自动的完成 编译 和 展示

    2. watch mode模式

    (1). 简介

      在该模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将重新编译我们不需要手动去运行 npm run build指令了;

    (2). 配置

    方法1:babel.config.js增加下面配置

    module.exports = {
        mode: "development",
        watch:true,     //开启监听
        entry: "./src/main.js"
    }

    方法2:在package.json中增加下面配置

      "scripts": {
        "build": "webpack --watch"
      },

    结果剖析:

      通过【npm run build】打包,运行index.html,这个时候在修改一下代码,自动编译映射。

      上述watch mode,可以监听到文件的变化,但是事实上它本身是没有自动刷新浏览器的功能的: 我们希望在不使用live-server(vs code插件)的情况下,可以具备live reloading(实时重新加载)的功能 ,这个使用就需要用下面的dev-server模式

    3. dev-server模式(推荐)

    (1). 步骤

     A. 安装loader:npm install webpack-dev-server -D

     B. 修改babel.config.js中的配置,增加target节点 、devServer节点。

    // 这里必须通过 commonjs的写法配置,不能写 ES6的写法
    const path = require('path');
    
    module.exports = {
        target: "web",
        mode: "development", // 设置模式, development 开发阶段, 会设置development, production 准备打包上线的时候, 设置production(默认)
        entry: "./src/main.js", //入口文件
        output: {
            path: path.resolve(__dirname, "./build"), //打包后存放路径, 必须写绝对路径
            filename: "bundle.js" //打包后的文件名称
        },
        devServer: {
            static: {
                directory: path.join(__dirname, 'public'),
            },                                              //表示从这个文件夹里找打包后没有找到的文件(打包是打src文件夹)
        },
        module: {
       
        },
    }

     C. 修改package.json中的配置,script标签中增加serve节点。

      "scripts": {
        "build": "webpack",
        "serve": "webpack serve"
      },

     D. 运行指令【npm run serve】,然后访问 http://localhost:8080/ ,然后F12打开调试器,修改代码,发现页面自动刷新了,输出最新的结果了。

    (2). 剖析 

        webpack-dev-server 在编译之后不会写入到任何输出文件(不会生成build文件夹),而是将 打包后 文件保留在内存中。

     事实上webpack-dev-server使用了一个库叫memfs(memory-fs webpack自己写的) 。

    二. dev-server的热替换

    1. 简介

    PS:

      特别是当页面有计数器的情况下,HMR更加适合,因为不需要刷新整个页面,计数器就不会被重置。

    2. 如何配置

    (1). babel.config.js中的配置

    devServer: {
            hot: true,
    }

    (2). 需要动态指定哪个模块需要HMR

    hmr.js代码如下:

    console.log('hmr11111122333');

    导入main.js中(必须先import导入,再配置)

    // 测试HMR
    import '@js/hmr';
    if (module.hot) {
      module.hot.accept("./js/hmr.js", () => {
        console.log("hmr模块发生更新了");
      })
    }

    (3). 运行结果

    3. HMR的原理

    (1). 说明

      vue开发中,我们使用vue-loader,此loader支持vue组件的HMR,提供开箱即用的体验;不需要单独配置module.hot.accept

    (2). 原理剖析

    三. dev-server的其它配置

    (更多配置参考官网:https://webpack.js.org/configuration/dev-server/)

    1. 基本配置

    (1). static→directory:用于设置后打包后寻找静态资源的文件夹

    (2). hot:设置为true,开启热替换。

    (3). host:设置主机地址,默认值为localhost

    (4). port:设置监听的端口,默认情况下是8080

    (5). open:是否打开浏览器。 默认值是false,设置为true会打开浏览器。

    (6). compress:是否为静态文件开启gzip compression: 默认值是false,可以设置为true;

    分享完整节点:

    module.exports = {
        target: "web",
        devServer: {
            static: {
                directory: path.join(__dirname, 'public'), //表示从这个文件夹里找打包后没有找到的文件(打包是打src文件夹)
            },
            hot: true,
            host: "localhost",
            port: 9999,
            open: true,
            compress: true
        },
    }

    补充几个本地地址的区别:

    2. 代理配置

    (1). 简介 

     proxy是我们开发中非常常用的一个配置选项,它的目的设置代理来解决跨域访问的问题:

     A. 比如我们的一个api请求是 http://localhost:8888,但是本地启动服务器的域名是 http://localhost:8000,这个时候发送网络请求就会出现跨域的问题

     B. 那么我们可以将请求先发送到一个代理服务器代理服务器和API服务器没有跨域的问题,就可以解决我们的跨域问题了

    PS:在实际开发中,跨域问题通常要配合服务器端一起解决,比如服务器端设置cors等等。

    (2). 常用配置  

     target:表示的是代理到的目标地址,比如 /api-hy/moment会被代理到 http://localhost:8888/api-hy/moment;

     pathRewrite:默认情况下,我们的 /api-hy 也会被写入到URL中,如果希望删除,可以使用pathRewrite;

     secure:默认情况下不接收转发到https的服务器上,如果希望支持,可以设置为false;

     changeOrigin:它表示是否更新代理后请求的headers中host地址;

     historyApiFallback是开发中一个非常常见的属性,它主要的作用是解决SPA页面在路由跳转之后,进行页面刷新时,返回404的错误。boolean值:默认是false。如果设置为true,那么在刷新时,返回404错误时,会自动返回 index.html 的内容;object类型的值,可以配置rewrites属性(了解):可以配置from来匹配路径,决定要跳转到哪一个页面;

    完整代码: 

    devServer: {
            static: {
                directory: path.join(__dirname, 'public'), //表示从这个文件夹里找打包后没有找到的文件(打包是打src文件夹)
            },
            hot: true,
            host: "localhost",
            port: 9999,
            open: true,
            compress: true,
            // 下面是代理相关的配置
            proxy: {
                "/api": {
                    target: "http://localhost:8888",
                    pathRewrite: {
                        "^/api": ""
                    },
                    secure: false,
                    changeOrigin: true
                }
            }
        },

    四. resolve模块解析

    1. 背景

     在开发中我们会有各种各样的模块依赖,这些模块可能来自于自己编写的代码,也可能来自第三方库;

     resolve可以帮助webpack从每个 require/import 语句中,找到需要引入到合适的模块代码

     webpack 使用 enhanced-resolve 来解析文件路径;

    2. webpack识别的3中路径

    (1). 绝对路径

     由于已经获得文件的绝对路径,因此不需要再做进一步解析

    (2). 相对路径

     在这种情况下,使用 import 或 require 的资源文件所处的目录,被认为是上下文目录;

     在 import/require 中给定的相对路径会拼接此上下文路径,来生成模块的绝对路径

    (3). 模块路径

     在 resolve.modules中指定的所有目录检索模块;

     A. 默认值是 ['node_modules'],所以默认会从node_modules中查找文件; 

     B. 我们可以通过设置别名的方式来替换初识模块路径,后面讲解alias的配置;

    3. 区分文件 or 文件夹

    (1). 如果是一个文件:

      A. 如果文件具有扩展名,则直接打包文件

      B. 否则,将使用 resolve.extensions选项作为文件扩展名解析(默认情况下,.js/wasm/mjs/json结尾的可以不用写后缀)

    (2). 如果是一个文件夹:

      会在文件夹中根据 resolve.mainFiles配置选项中指定的文件顺序查找;

      A. resolve.mainFiles的默认值是 ['index']

      B. 再根据 resolve.extensions来解析扩展名

    4. 代码配置(重要)

    (1). extensions是解析到文件时自动添加扩展名:

      默认值是 ['.wasm', '.mjs', '.js', '.json'];所以如果我们代码中想要添加加载 .vue 或者 jsx 或者 ts 等文件时,我们必须自己写上扩展名;

    (2). 配置别名alias:

      特别是当我们项目的目录结构比较深的时候,或者一个文件的路径可能需要 ../../../这种路径片段;我们可以给某些常见的路径起一个别名;

    配置分享

        resolve: {
            extensions: [".js", ".json", ".mjs", ".vue", ".ts", ".jsx", ".tsx"],   //vue ts jsx tsx 是自己添加的后缀
            alias: {
                "@": path.resolve(__dirname, "./src"),
                "@js": path.resolve(__dirname, "./src/js")
            }
        },

    代码实战改写 

    // commonjs的导入
    // const { getPrice } = require('./js/t1');
    // 配置resolve后的写法
    const { getPrice } = require('@js/t1');
    
    // ES6的导入
    // import { sum } from './js/t2.js';
    // 配置resolve后的写法
    import { sum } from '@js/t2';
    
    // 导入创建html的js
    // import './js/createHtml.js';
    // 配置resolve后的写法
    import '@js/createHtml';
    
    // 导入需要babel转换的js文件
    // import './js/ypf1.js';
    // 配置resolve后的写法
    import '@js/ypf1';// 调用vue相关
    import {createApp} from 'vue';
    // import App from './vue/App.vue';
    // 配置resolve后的写法
    import App from '@/vue/App';
    

    五. 区分开发/生产环境实战

    1. 实现思路

     编写多个的配置文件,开发和生成时,分别加载不同的配置文件即可。

    2. 实现步骤

    (1). 通过指令安装下面程序,用于合并js文件

     【npm install webpack-merge -D

    (2). 在config目录下新建3个js文件:

    webpack.common.conf.js (公共的)

    // 这里必须通过 commonjs的写法配置,不能写 ES6的写法
    const path = require('path');
    // 导入插件
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const { DefinePlugin } = require("webpack");
    const { VueLoaderPlugin } = require('vue-loader/dist/index');
    
    module.exports = {
        target: "web",
        entry: "./src/main.js", //入口文件
        output: {
            path: path.resolve(__dirname, "../build"), //打包后存放路径, 必须写绝对路径
            filename: "bundle.js" //打包后的文件名称
        },
        resolve: {
            extensions: [".js", ".json", ".mjs", ".vue", ".ts", ".jsx", ".tsx"], //vue ts jsx tsx 是自己添加的后缀
            alias: {
                "@": path.resolve(__dirname, "../src"),
                "@js": path.resolve(__dirname, "../src/js")
            }
        },
        module: {
            rules: [{
                    test: /.css$/, //(表示所有的css结尾的文件都进行匹配)
                    use: ["style-loader", "css-loader"]
                },
                {
                    test: /.less$/, //(表示所有的less结尾的文件都进行匹配)
                    use: ["style-loader", "css-loader", "less-loader"]
                },
                // 文件解析功能,"asset/resource"(等价于file-loader )
                {
                    test: /.(jpe?g|png|gif|svg)$/,
                    type: "asset/resource",
                    generator: {
                        filename: "img/[name]_[hash:6][ext]"
                    }
                },
                // 解析字体(也可以用file-loader处理)
                {
                    test: /.(eot|ttf|woff2?)$/,
                    type: "asset/resource",
                    generator: {
                        filename: "font/[name]_[hash:6][ext]"
                    }
                },
                // Vue解析打包
                {
                    test: /.vue$/,
                    loader: "vue-loader"
                }
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: "./public/index.html",
                title: "hello webpack"
            }),
            new DefinePlugin({
                BASE_URL: "'./'",
                __VUE_OPTIONS_API__: true,
                __VUE_PROD_DEVTOOLS__: false
            }),
            new VueLoaderPlugin()
        ]
    }
    View Code

    注意:

      (1). entry: "./src/main.js", 入口文件的位置,不需要改成 ../src/main.js,因为入口文件其实是和另一个属性时有关的 context,默认应该是webpack的启动目录(当前路径)

      (2). 其它的地方需要相应的改变路径,比如:path: path.resolve(__dirname, "../build"),

    webpack.dev.conf.js(开发时用)

    const path = require('path');
    const { merge } = require('webpack-merge');
    const commonConfig = require('./webpack.common.conf');
    
    module.exports =merge(commonConfig, {
        mode: "development", // 设置模式, development 开发阶段, 会设置development, production 准备打包上线的时候, 设置production(默认)
        devtool: "source-map", // 设置source-map, 建立js映射文件, 方便调试代码和错误(配合上面的development)
        devServer: {
            static: {
                directory: path.join(__dirname, 'public'), //表示从这个文件夹里找打包后没有找到的文件(打包是打src文件夹)
            },
            hot: true,
            host: "localhost",
            port: 9999,
            open: true,
            compress: true,
            // 下面是代理相关的配置
            // proxy: {
            //     "/api": {
            //         target: "http://localhost:8888",
            //         pathRewrite: {
            //             "^/api": ""
            //         },
            //         secure: false,
            //         changeOrigin: true
            //     }
            // }
        },
    })
    View Code

    注意:

      需要合并webpack.common.conf.js  

    webpack.pro.conf.js(生产时用)。

    const { CleanWebpackPlugin } = require("clean-webpack-plugin");
    const CopyWebpackPlugin = require('copy-webpack-plugin');
    const { merge } = require('webpack-merge');
    const commonConfig = require('./webpack.common.conf');
    
    module.exports =merge(commonConfig, {
        mode: "production", // 设置模式, development 开发阶段, 会设置development, production 准备打包上线的时候, 设置production(默认)
        plugins: [
            new CleanWebpackPlugin(),
            new CopyWebpackPlugin({
                patterns: [{
                    from: "public", //设置从哪一个源中开始复制;
                    // to: "./",    //复制到的位置,可以省略,会默认复制到打包的目录下;
                    globOptions: { //下面配置忽略清单
                        ignore: [
                            "**/index.html"
                        ]
                    }
                }]
            }),
        ]
    })
    View Code

    注意:

      需要合并webpack.common.conf.js  

    (3). 在package.json中修改相关指令的调用文件

      "scripts": {
        "build": "webpack --config ./config/webpack.pro.conf.js",
        "serve": "webpack serve --config ./config/webpack.dev.conf.js"
      },

    (4). 测试 

      【npm run build】打包

     【npm run serve】开发运行

     

     

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    LTE
    LTE
    LTE
    LTE
    LTE DL-SCH and PDSCH Processing Chain
    LTE PDSCH Port 5 UE-Specific Beamforming
    推荐我的公众号
    GitHub Top 微信小程序
    深度前馈网络
    考研经历吐血总结
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/15317553.html
Copyright © 2011-2022 走看看