> 文章列表 > 20230418----重返学习-模块化开发-webpack打包

20230418----重返学习-模块化开发-webpack打包

20230418----重返学习-模块化开发-webpack打包

day-052-fifty-two-20230418-模块化开发-webpack打包

模块化开发

  • 用于确保文件引入的顺序

单例设计模式

  • 单例设计模式:如果有多个模块,模块之间存在依赖关系,那我们在html中导入js的时候,我们需要注意模块之间的依赖关系【模块依赖关系越复杂,就会变得很恶心】

    • 例子

      • ./index.html

        <!DOCTYPE html>
        <html lang="en">
        <head><meta charset="UTF-8">
        </head>
        <body><h1>11111</h1><script src="./ModelA.js"></script><script src="./ModelB.js"></script><script src="./index.js"></script>
        </body>
        </html>
        
      • ./ModelA.js

        let ModelA = (() => {//求和const sum = function sum(...args) {return args.reduce((res, item) => res + item, 0);};return {sum,};
        })();
        
      • ./ModelB.js

        let ModelB = (() => {//求平均值const average = function average(...args) {let sum = ModelA.sum(...args);return sum / args.length;};return {average,};
        })();
        
      • ./index.js

        // console.log(`ModelA-->`, ModelA);
        console.log(`ModelA.sum(1,2,3,4,5,6,7)-->`, ModelA.sum(1, 2, 3, 4, 5, 6, 7));
        console.log(`ModelB.average(1,2,3,4,5,6,7)-->`, ModelB.average(1, 2, 3, 4, 5, 6, 7));
        
    • 优点

      • 兼容性好
      • 依赖关系简单
  • 普通的script标签导入关系,只适合做简单的依赖关系,复杂的如相互循环关系不行

    A B C
    B依赖A
    C依赖B和A
    
      <script src="./ModelA.js"></script><script src="./ModelB.js"></script><script src="./index.js"></script>
    
    • 如:

      A B C D
      B依赖A和C
      C依赖A和D
      D依赖B和C
      

AMD(require.js)

AMD是依赖于require.js的写法,不过目前2023年差不多被淘汰了。

  • AMD思想,是在单例模式的基础上,实现了依赖的管控【管控模式:依赖前置 在具体开发模块代码之前,把所有需要的依赖先处理好,再去开发】

    • 依赖于其它模块并导出当前模块

      define(['A', 'B'], function (A, B) {'use strict';return {}
      });
      
  • grunt、glup、fis都是基于AMD思想开发的

  • 例子:

  • ./index.html

    <!DOCTYPE html>
    <html lang="en">
    <head><meta charset="UTF-8">
    </head>
    <body><h1>AMD</h1><script src="./require.min.js"></script><script src="./index.js"></script>
    </body>
    </html>
    
  • ./ModelA.js

    //导出文件--模块A不依赖于其它模块
    define(function (require, factory) {"use strict";return {sum(...args) {return args.reduce((res, item) => res + item, 0);},};
    });
    
  • ./ModelB.js

    //导出文件--模块B依赖于模块A
    define(["ModelA"], function (ModelA) {return {average(...args) {let sum = ModelA.sum(...args);return sum / args.length;},};
    });
    
  • ./index.js

    //导入文件,依赖于模块A与模块B
    require(["ModelA", "ModelB"], function (ModelA, ModelB) {console.log(`ModelA-->`, ModelA);console.log(`ModelA.sum(1,2,3,4,5)-->`, ModelA.sum(1, 2, 3, 4, 5));console.log(`ModelB.average(1,2,3,4,5)-->`, ModelB.average(1, 2, 3, 4, 5));
    });
    

CMD

  • CMD解决了前置依赖的问题CommonJS(后台–nodejs)/sea.js(前端)
  • nodejs --> 前端的JavaScript
    • 基本上支持所有的JavaScript的语法
    • 命令提示符(黑框) --> node XXX.js
  • node写法
    • ./ModelA.js

      //导出 单个值
      module.exports = function sum(...args) {return args.reduce((res, item) => res + item, 0);
      };
      
    • ./ModelB.js

      let sum = require("./ModelA");function average(...args) {let res = sum(...args);return res / args.length;
      }
      let num = 100;//导出,导出多个内容
      module.exports = {average,num,
      };
      
    • ./index.js

      // console.log(`111-->`, 111);let path = require("path"); //node.js自身的模块
      //导入,可以省略`.js`后缀,不能省略`./`(自己创建的模块);不加`./`,默认是node.js自己的模块。
      // 导入单个内容
      let sum = require("./ModelA");
      console.log(`sum-->`, sum);//sum--> [Function: sum]
      console.log(`sum(1,2,3,4,5)-->`, sum(1, 2, 3, 4, 5));//sum(1,2,3,4,5)--> 15// 导入多个内容
      // let ModelB=require('./ModelB')
      // console.log(`ModelB-->`, ModelB);// 导入多个内容,可以使用解构赋值
      let { average, num } = require("./ModelB");
      console.log(`average(1,2,3,4,5)-->`, average(1, 2, 3, 4, 5));//average(1,2,3,4,5)--> 3
      console.log(`num-->`, num);//num--> 100
      
    • 要用node.js中的node xxx命令来执行,这里在index.js所在目录中使用node index.js来调用。

ES6中的Module

  1. 在script标签中设置type=“module”,可以直接在浏览器端开启ES6Module规范,并且需要保证页面是基于HTTP/HTTPS协议预览–即本地开发要使用类似于Live Server的本地服务器

    <script type="module" src="main.js"></script>
    
  2. ES6Module规范

    1. 模块中只想导出一次,直接基于 export default 导出即可

      • 导出:

        export default sum; 
        export default {sum:sum};  
        
      • 导入:

        import sum from './A';
        
        import A from './A';  //=> A.sum(); 
        
      • 不能直接给A解构赋值import {sum} from './A';这样报语法错误

    2. 模块中想导出多次,导出多个方法(或者变量)给外面用 export

      • 导出:export {sum}; 直接导出对象是不支持的,或者obj={},我们export obj这样也不支持,像这样做需要使用 export default 处理

        export let num = 10;
        export function fn() {};
        
      • 导入:import A from './A'; 这样写也是不行的,这种方式只支持 export default

        import {num,fn} from './A';
        import * as C from './C.js';   => C.fn();
        
    3. 原理:浏览器底层机制,ES6中模块导出是按照导出一个对象处理的

      • 基于export导出的时候,浏览器底层机制,是按照导出一个对象处理的,每一次调用,都是设置为一个键值对。
        • import {num,fn} from './A',导出语句如果遇到了花括号,后面导出的值就是导出对象
        • import * as ModelA from './A',导出语句如果遇到了*号,后面导出的值就是导出对象,as表示设置别名。
      • 基于export default导出的东西,是赋值给导出对象default属性的。
        • import ModelA from './A',导出语句如果没遇到花括号,后面导出的值就是导出对象.default
     {// 基于 export 导出的内容,直接设置为键值对 num:10,fn(){},// 基于 export default 导出的东西,是赋值给对象的default属性的default:xxx}
    
    • 所以,当我们基于import的时候,如果后面是一个花括号的形式,就意为解构赋值,就是对整个模块进行解构。而如果是一个对象时,就意为接收了模块.default这个属性。
      1. 为啥 export default 导出的不能直接结构赋值?

        • 因为它导出的内容,在对象的default属性中,我们需要先拿到default属性的值,才能解构处理…

          import A from './A';  //->  会默认让 A=`对象.default `
          let {sum} = A;
          
      2. 为啥 export 导出的可以解构赋值?

        • 因为它是直接把内容付给对象的键值对,导入的时候直接解构即可…
      3. import * as X from ‘./A’; 此时它会把导出的对象赋值给X

  • 例子:
    • ./index.html

      <!DOCTYPE html>
      <html lang="en">
      <head><meta charset="UTF-8">
      </head>
      <body><h1>ES6 Module</h1><script type="module" src="./index.js"></script>
      </body>
      </html>
      
    • ./index.js

      //不可以省略后缀.js
      // import ModelA from "./ModelA.js";
      // console.log(`ModelA-->`, ModelA);//注意 `export default`导出的不能直接解构赋值,但可以用一个变量A接收之后再另起一行对变量A进行解构。
      //是因为底层对`export default`导出的东西放在`{default:{}}`中的`default属性`里了。
      // import {sum,num} from "./ModelA.js";
      // console.log(`num-->`, num);import {default as theDefault} from "./ModelA.js"
      console.log(`theDefault-->`, theDefault);
      // import {'default':{sum,num}} from "./ModelA.js"//default是关键字,会报错
      // console.log(`theDefault-->`, theDefault);// import ModelA from "./ModelA.js";
      // console.log(`ModelA-->`, ModelA);
      // let { sum, num } = ModelA;
      // console.log(`num-->`, num);import { average } from "./ModelB.js";
      console.log(`average-->`, average);// import { a, b, c } from "./ModelC.js";
      // console.log(`a, b, c-->`, a, b, c);//*表示导入全部,as表示起个别名
      import * as ModelC from "./ModelC.js";//代码运行的时候,import会自动提升到顶部,所以随意写位置都可以。
      console.log(`ModelC.a, ModelC.b, ModelC.c-->`, ModelC.a, ModelC.b, ModelC.c);
      
    • ./ModelA.js

      //单个导出---一个js文件可以出现多次
      // export// 多个导出--一个js文件只能出现一次
      // export defaultconst sum = function sum(...args) {return args.reduce((res, item) => res + item, 0);
      };
      let num = 100;
      export default {sum,num,
      };
    • ./ModelB.js

      import ModelA from "./ModelA.js";export function average(...args) {let sum = modelA.sum(...args);return sum / args.length;
      }
    • ./ModelC.js

      export const a = 100;
      export const b = 200;
      export const c = 300;// 可以声明变量,值为对象
      export const obj = {n: 100,m: 200,
      };// export不能直接导出对象
      // export {
      //   nn:100,
      //   mm:200
      // }
      // 能直接导出对象的是`export default`
      export default {nn: 100,mm: 200,
      };
      // `export default`可以与`export`共存,但不能出现多次,即整个js文件中只能出现一次`export default`。
      

webpack打包

  1. 安装webpack

    1. 本地安装

      npm i webpack@5.49.0 webpack-cli@4.7.2
      
      • 好处:不同的文件夹可以安装不同的版本,一般就本地安装

      • 本地安装之后使用的方法

        • *方法1:

          1. 找到 package.json —> “scripts” 配置脚本命令

            • 名称可以自定义
            {"scripts": {"自定义脚本命令":"webpack"}
            }
            
          2. 在对应目录中执行配置的脚本命令

            npm run 自定义脚本命令
            // npm run serve//如果package.json中的"scripts"配置的scripts下的属性名称为serve
            
        • *方法2:

          1. 在对应目录中执行配置的脚本命令npx webpack (webpack5版本及以上)

            npx webpack
            
      • 默认打包的js是src目录下index.js为入口文件----》dist文件夹main.js为打包后的出口文件

    2. 全局安装

      npm i webpack@5.49.0 webpack-cli@4.7.2 -g    //安装
      npm uni webpack@5.49.0 webpack-cli@4.7.2 -g  //删除
      
      • 缺点:只能安装某一个版本

      • 全局安装之后可以在全局环境中的命令终端中使用命令 webpack

        webpack    //webpack执行
        
    • webpack一般要高一个版本,webpack-cli一般要低一个版本。如果相同版本,一般会报兼容性错误。
  2. 使用webpack

    • 本地安装之后使用的方法

      • *方法1:

        1. 找到 package.json —> “scripts” 配置脚本命令

          • 名称可以自定义
          {"scripts": {"自定义脚本命令":"webpack"}
          }
          
        2. 在对应目录中执行配置的脚本命令

          npm run 自定义脚本命令
          // npm run serve//如果package.json中的"scripts"配置的scripts下的属性名称为serve
          
      • *方法2:

        1. 在对应目录中执行配置的脚本命令npx webpack (webpack5版本及以上)

          npx webpack
          
    • 默认打包的js是src目录下index.js为入口文件----》dist文件夹main.js为打包后的出口文件

    • webpack 支持默认配置打包:不需要自己写配置项,它默认有自己的配置项,当我们执行 webpack 的时候,会按照默认配置项打包

      • 找到当前项目目录中的 src文件夹,找到文件夹中的index.js文件,作为入口,按照入口文件夹中的代码和模块依赖,最后进行打包,打包后的文件,放在dist目录中
  3. 自定义webpack打包的规则

    • 自己配置webpack的配置文件。如设置入口与出口

      1. 在根目录中新建 webpack.config.js
        • 当我们执行webpack命令的时候,首先检测有没有这个文件。

          • 有,就按照这个文件提供的规则进行打包编译;
          • 没有,就按照默认的规则进行打包编译。
        • 简单的配置项示例

          const path = require("path"); //node.js的路径模块module.exports = {entry: "./src/index.js", //入口文件路径//出口相关配置output: {filename: "index.js", //文件名称path: path.resolve(__dirname, "dist"), //在当前项目的根目录下,创建一个dist文件夹。},mode: "development", //development 开发环境-文件不会压缩,production 生产环境-会压缩
          };
          
          • 实际意思,该文件是一个js文件,以node.js的方式来执行。通过module.exports抛出一个表示webpack配置对象的对象。
            • 由于是node.js的文件,故而可以引入node.js的一些插件及组件,来根据不同情况做不同处理。

              const path = require("path"); //node.js的路径模块
              let outputPath = path.resolve(__dirname, "dist"); //在当前项目的根目录下,创建一个dist文件夹。
              console.log(`打包完成后输出的目录:outputPath-->`, outputPath);// 这个导出的对象,就是webpack的配置;
              module.exports = {entry: "./src/index.js", //入口文件路径//出口相关配置output: {filename: "index.js", //文件名称path: outputPath, //输出文件的目录},mode: "production", //development开发环境-文件不会压缩,production生产环境-会压缩
              };
              
    • 不同环境的配置

      • 执行不同的脚本命名做不同的打包,分成开发环境和生产环境
        • 方法1 做多个不同的配置执行文件,每一个配置文件对应不同的环境
          1. 在根目录制定做多个不同的配置执行文件,每一个配置文件对应不同的环境

            • webpack.config.development.js

              const path = require("path");
              let outputPath = path.resolve(__dirname, "dist");
              console.log(`打包完成后输出的目录:outputPath-->`, outputPath);module.exports = {entry: "./src/index.js",output: {filename: "index.[hash].js",path: outputPath,},mode: "development", //development开发环境-文件不会压缩,production生产环境-会压缩
              };
              
            • webpack.config.production.js

              const path = require("path");
              let outputPath = path.resolve(__dirname, "dist");
              console.log(`打包完成后输出的目录:outputPath-->`, outputPath);
              module.exports = {entry: "./src/index.js",output: {filename: "index.[hash].min.js",path: outputPath,},mode: "production", //生产环境-会压缩
              };
              
            • 这个是公司常用的,一个配置对应一个环境,更简洁。逻辑更好理解。

          2. 在package.json中的"scripts"定义的不同执行命令中,使用webpack --config来指定webpack的配置执行文件。

        • 方法2 :相同的配置文件,只不过根据环境变量不同,做一些不同的配置而已。
          • 使用cross-env,拿到cross-env命令执行时配置的NODE_ENV属性的值,根据NODE_ENV属性的值,对同一个配置文件进行处理。
            1. 安装cross-env

              npm i cross-env --save-dev
              
            2. 在package.json中的"scripts"定义的不同执行命令中,使用cross-env NODE_ENV=xxxx来指定NODE_ENV的值。

              {"scripts": {"开发": "cross-env NODE_ENV=development webpack","D": "cross-env NODE_ENV=development webpack","生产": "cross-env NODE_ENV=production webpack","P": "cross-env NODE_ENV=production webpack",},
              }
              
            3. 在webpack.config.js中通过process.env.NODE_ENV拿到使用cross-env脚本执行时设置的NODE_ENV的值,并根据它对module.exports要返回出去的对象的值进行处理。

              const path = require("path"); //node.js的路径模块
              let outputPath = path.resolve(__dirname, "dist"); //在当前项目的根目录下,创建一个dist文件夹。
              // console.log(`打包完成后输出的目录:outputPath-->`, outputPath);let NODE_ENV = process.env.NODE_ENV || `production`;
              const theMode = NODE_ENV;
              let outputFilename = `production.js`;
              console.log(`NODE_ENV-->`, NODE_ENV);
              console.log(`process.env.NODE_ENV-->`, process.env.NODE_ENV);if (NODE_ENV === `development`) {outputFilename = `development.js`;
              }// 这个导出的对象,就是webpack的配置;
              module.exports = {entry: "./src/index.js", //入口文件路径//出口相关配置output: {filename: outputFilename, //文件名称path: outputPath, //输出文件的目录},mode: theMode, //development开发环境-文件不会压缩,production生产环境-会压缩
              };
              
            4. 命令终端窗口打开调用package.json中脚本命令

              npm run D
              
  4. 生成html文件

    1. 安装html插件,用于在webpack中可以生成一个html文件。
  5. 配置开发服务器

  6. 清除旧文件

    1. 安装clean-webpack-plugin插件

      npm i clean-webpack-plugin –save-dev
      
    2. 在webpack.config.js中插入该插件。

      const pluginsList = [];//"clean-webpack-plugin"用于清除之前打包后文件,再打包新的文件。
      // 1. 导入插件
      let { CleanWebpackPlugin } = require("clean-webpack-plugin");
      const theCleanWebpackPlugin = new CleanWebpackPlugin();
      pluginsList.push(theCleanWebpackPlugin);module.exports = {plugins: pluginsList, //2. 使用插件,是一个数组。
      }
      
    3. 在package.json中scripts脚本中配置webpack命令

      {"scripts": {"build": "webpack --config webpack.config.production.js",//这个是打包命令,在出口文件目录产生文件,配置好了clean-webpack-plugin后会清除上次的出口文件"D": "cross-env NODE_ENV=development webpack",//这个是打包命令,在出口文件目录产生文件,配置好了clean-webpack-plugin后会清除上次的出口文件"fang": "webpack",//这个是打包命令,在出口文件目录产生文件,配置好了clean-webpack-plugin后会清除上次的出口文件"fang": "webpack server"//这个是调用本地服务器,不会在出口文件目录产生文件,不会清除上次的出口文件},
      }
      
    4. 命令终端窗口打开调用package.json中脚本命令

      npm run build
      

进阶参考

妊娠纹产品