项目构建工具笔记

1.项目构建工具(所有构建工具,都是基于nodejs平台去构建的)
    1)gulp
        基于 Nodejs 流的构建工具    
        1.下载安装
            全局安装 gulp
              npm i gulp-cli -g
            本地安装 gulp
              npm init -y 初始化包描述文件(注意包名不能为gulp)
              npm i gulp -D    
              npm install --save-dev gulp-babel @babel/core @babel/preset-env 将ES6模块化编译成commonjs模块化,让代码可在浏览器运行
              npm install --save-dev gulp-browserify 将commonjs模块化编译成浏览器能识别的模块化
              npm i gulp-rename -D 可对babel/browserify/less 解析的文件进行重命名(可选择)
              npm i gulp-less -D 将less文件编译成css文件
              npm install --save-dev gulp-connect 自动刷新浏览器
              
            找 gulp 插件
                https://gulpjs.com/plugins/ 更新可能会不及时
                https://www.npmjs.com/ 最全
        2.使用
            定义 gulp 的配置文件:gulpfile.js
            当你 gulp 指令时会读取配置文件的配置,从而执行任务
        3.gulp方法(存在链式调用)
            gulp.src("src/js/*.js"): 相当于可读流,读取文件,*代表读取选中目录下的所有js文件
            gulp.dest("dist/js"):相当于可写流,写入文件,将可读流中文件输出到"dist/js"目录下
            gulp.task("babel", () => {}):注册任务,第一个参数babel是任务名称, 第二个参数是任务回调函数
            series():同步执行任务babel和browserify
                gulp.task('js', gulp.series(['babel', 'browserify']));
            parallel():异步/并行执行任务js和less
                gulp.task('dev', gulp.parallel(['js', 'less']));
            watch():监视文件,执行后续文件
                 // 监视 './src/js/目录下所有js文件,一旦文件发生变化,会执行后续任务,还可以健身less和html文件
                  gulp.watch("./src/js/*.js", gulp.series("js"));
        4.配置文件gulpfile.js内容
            //引入模块
            const gulp = require("gulp");
            const babel = require("gulp-babel");
            const browserify = require("gulp-browserify");
            const rename = require("gulp-rename");
            const less = require("gulp-less");
            const connect = require("gulp-connect");
            const open = require("./open");            
            // 注册任务
            // gulp.task(任务名称, 任务回调函数)
            gulp.task("babel", () => {
!!              // 必须加return,否则报错
              return gulp
                .src("src/js/*.js") // 将src/js/目录下所有js文件读取
                .pipe(
                  // 对可读流中的文件数据进行babel处理
                  babel({
                    presets: ["@babel/preset-env"]
                  })
                )
                .pipe(gulp.dest("dist/js")) // 将可读流中文件输出到"dist/js"目录下
!!                .pipe(connect.reload()); //执行完任务自动刷新浏览器
            });
            
            gulp.task("browserify", function() {
              // 必须加return 否则报错
              return gulp
                .src("dist/js/index.js") // 将src/js/目录下所有js文件读取
                .pipe(browserify()) // 将Commonjs模块化编译成浏览器能识别的模块化
!!                .pipe(rename("dist.js")) // 对流中的文件进行重命名
                .pipe(gulp.dest("dist/js")) // 将可读流中文件输出到"dist/js"目录下
                .pipe(connect.reload()); //执行完任务自动刷新浏览器
            });
            
            gulp.task("less", function() {
              return gulp
                .src("src/less/*.less") // 将src/less/目录下所有less文件读取
                .pipe(less()) // 将less编译成css
                .pipe(gulp.dest("dist/css"))// 将可读流中文件输出到"dist/css"目录下
                .pipe(connect.reload()); //执行完任务自动刷新浏览器
            });
            
            gulp.task("html", function() {
              return gulp
                .src("src/index.html") //将src目录下index.html文件读取
                .pipe(gulp.dest("dist")) //将可读流中文件输出到"dist"目录下
                .pipe(connect.reload()); //执行完任务自动刷新浏览器
            });
            
            // 配置自动化任务
            gulp.task("watch", () => {
              // 开启服务器
              connect.server({
                root: "dist", // 运行代码根目录
                port: 3000,
                livereload: true // 自动刷新浏览器
              });
            
              // 自动打开浏览器
              open("http://localhost:3000");
            
              // 监视文件
              // 自动编译
              // 监视 './src/js/*.js' 文件,一旦文件发生变化,会执行后续任务
              gulp.watch("./src/js/*.js", gulp.series("js"));
              gulp.watch("./src/less/*.less", gulp.series("less"));
              gulp.watch("./src/index.html", gulp.series("html"));
            });
            // 配置统一任务
            // 当你执行js任务时,实际上执行的是  gulp.series 任务,
            // 而 gulp.series 任务实际上执行的是 'babel', 'browserify'
            // 特点是:同步执行  一般会慢一点
            gulp.task("js", gulp.series(["babel", "browserify"]));
            // 特点是:异步/并行执行 一般会快一点
            gulp.task("dev", gulp.parallel(["js", "less", "html"]));
!!            gulp.task("start", gulp.series(["dev", "watch"]));
        5.运行任务
            gulp start(任务名)
        
    2)webpack
        1.什么是webpack
            webpack 是一个现代 JavaScript 应用程序的静态模块打包工具。
            当 webpack 处理应用程序时,它为根据入口js文件在内部构建一个 依赖图(dependency graph),
            此依赖图会映射项目所需的每个模块,并生成一个或多个 bundle(打包机)。    
        2.核心概念
            1) entry 入口
                以某个文件为入口开始打包            
            2) output 输出            
                      打包后资源输出到哪里去            
            3) loader 加载器            
                webpack 本身只能识别 json、js 模块,其他模块(less)一旦加载就会报错
                      需要借助 loader 帮助 webpack 识别它识别不了的模块            
            4) plugins 插件            
                loader 功能有限,要想做功能更加强大的工作交给插件            
            5) mode            
                      打包模式:开发环境(development)和生产环境(production)
        3.下载安装
            npm i webpack webpack-cli -g
            npm init -y
            npm i webpack webpack-cli -D
        4.用法
            1)直接使用
                1.运行 webpack 指令:`webpack ./src/js/index.js -o ./dist/js/index.js --mode=development`
                    以 webpack 的开发环境运行,处理 ./src/js/index.js 文件(index.js所依赖的模块也会自动被处理),输出到 ./dist/js/index.js
                       功能
                        能将 ES6 模块化编译成浏览器识别的语法,打包后可直接在浏览器运行
                
                运行 webpack 指令:`webpack ./src/js/index.js -o ./dist/js/index.js --mode=production`
                    以 webpack 的生产环境运行,处理 ./src/js/index.js 文件(index.js所依赖的模块也会自动被处理),输出到 ./dist/js/index.js
                       功能
                        能将 ES6 模块化编译成浏览器识别的语法
                        压缩 js 代码
                        打包后可直接在浏览器运行
                                        
                2.问题:
                          不能识别其他模块(less\css\html\jpg...
            2)通过配置config文件夹使用
                 
                 webpack.dev.js:配置开发环境,运行指令npm start
                webpack.prod.js:配置生产环境,运行指令serve build
                1.下载安装
                    npm i less less-loader style-loader css-loader -D 打包less
                    npm install --save-dev html-webpack-plugin 打包html(注意插件需要引入使用)
                    npm i url-loader file-loader -D 打包less图片,file-loader还可解决其它文件问题,如iconfont
                    npm i html-loader -D 解决 html 中 img src 为 解析前路径的问题,找不到资源
                    npm i webpack-dev-server -D 用来配置自动化,如自动打开浏览器等
                    配置生产环境额外下载的包:
                        npm i mini-css-extract-plugin -D 从js文件中找到css字符串,提取css成单独css文件,支持按需加载
                        npm i optimize-css-assets-webpack-plugin -D 压缩css的插件
                        npm i clean-webpack-plugin -D 自动删除由于重命名在打包的重复文件
!!!!                    npm i serve -g 解决生产环境css和html图片路径问题,需要下载一个全局服务器
                        
                2.配件package.json
                    // 启动项目的指令
                    "scripts": {
                        // npm start
                        // 本地下载的有些包会将运行的脚本添加到 node_modules/.bin 目录中
                        // 在start指令运行时,会将node_modules/.bin临时添加为环境变量
                        "start": "webpack-dev-server --config ./config/webpack.dev.js",
                        // 除了start,其余指令都得加 run
                        // run的作用就是将node_modules/.bin临时添加为环境变量
                        // npm run build
                        "build": "webpack --config ./config/webpack.prod.js"
                      },
                3.webpack.dev.js配置文件内容:
                    const { resolve } = require("path");
                    // 配置html插件需要引入使用,而loader不需要引入
                    const HtmlWebpackPlugin = require("html-webpack-plugin");                    
                    module.exports = {
                      // entry,指定入口文件
                      entry: "./src/js/index.js",
                      // output,指定js输出文件
                      output: {
                        path: resolve(__dirname, "../build"), // 输出目录
                        filename: "build.js" // 输出文件名
                      },
                      // loader
                      module: {
                        rules: [
                          // 所有loader的配置
                          {
                              // less配置
                            // 检查是否是less文件
                            test: /\.less$/,
                            // 如果满足test要求,文件就会通过use来处理
                            use: [
                              // use数组执行顺序:从下到上,从右到左
                              {
                                loader: "style-loader" // 从js文件中找到css字符串,并创建style标签插入页面中
                              },
                              {
                                loader: "css-loader" // 将 CSS 转化成 字符串,会以 CommonJS 模块化整合js文件中
                              },
                              {
                                loader: "less-loader" // 将 Less 编译成 CSS
                              }
                            ]
                          },
                          {
                            // 图片loader配置(默认是不能处理html的图片,让html加载图片)
                            test: /\.(png|jpg|gif)$/,
                            loader: "url-loader",
                            options: {
                              /*
                                11kb以下的图片会被base64(图片转成字符串格式)处理 
                                        优点:图片不会发送额外的请求,随着html文件一起被请求下来(减少服务器压力)
                                        缺点:体积会变的更大
                                        所以一般针对小图片来做
                              */
                              limit: 11000,
                              // 打包后的图片名处理
                              // 图片名是一串很长的hash值,[hash:10] hash值取10位
                              // [ext] 原来文件扩展名是啥就是啥
                              name: "[hash:10].[ext]",
                              // 关闭ES6模块化,使用Commonjs模块化
                              // 解决 html 中 img src 为 [object Module]
                              esModule: false,
                            }
                          },
                          {// 解决 html 中 img src 为 解析前路径的问题,找不到资源
                            test: /\.(html)$/,
                            loader: "html-loader"
                          },
                          {
                            // 排除文件
                            // 解决其它资源,如iconfont
                            exclude: /\.(less|jpg|png|gif|js|html)$/,
                            loader: 'file-loader',
                            options: {
                              // 打包后的图片名处理
                              // 图片名是一串很长的hash值,[hash:10] hash值取10位
                              // [ext] 原来文件扩展名是啥就是啥
                              name: "[hash:10].[ext]",
                            }
                          }
                        ]
                      },
                      // plugins
                      plugins: [
                          // html文件配置
                        new HtmlWebpackPlugin({
                          // 以 './src/index.html' 为模板创建新的html文件
                          // 新html文件结构和原来一样 并且 会自动引入webpack打包生成的js/css资源
                          template: "./src/index.html"
                        })
                      ],
                      // mode
                      mode: "development", // 开发环境
                      // 用来配置自动化,如自动打开浏览器等
                      devServer: {
                        contentBase: resolve(__dirname, '../build'), // 运行(构建后)代码的根目录
                        compress: true, // 启动gzip压缩
                        port: 3000, // 端口号
                        host: 'localhost', // 主机名
                        open: true, // 自动打开浏览器
                        overlay: false, // 不要webpack错误在浏览器全屏提示
                        quiet: true, // 让打包打印信息简短一些
                      }
                    };
                4.webpack.prod.js配置文件内容:
                    //引入1模块
                    const { resolve } = require("path");
                    // 插件需要引入使用,而loader不需要引入
                    // 打包html文件插件
                    const HtmlWebpackPlugin = require("html-webpack-plugin");
                    // 将css文件从js文件中分离插件
                    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
                    // 将css文件压缩插件
                    const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
                    // 删除重名后打包的多余文件
                    const { CleanWebpackPlugin } = require("clean-webpack-plugin");
                    
                    module.exports = {
                      // entry
                      entry: "./src/js/index.js",
                      // output
                      output: {
                        path: resolve(__dirname, "../build"), // 输出目录
                        filename: "static/js/build.js", // 输出文件名,如果是[name].js.那么默认输出文件名为main.js
                          publicPath: '/', // 公共引入资源路径
                      },
                      // loader
                      module: {
                        rules: [
                          // 所有loader的配置
                          {
                            // 检查是否是less文件
                            test: /\.less$/,
                            // 如果满足test要求,文件就会通过use来处理
                            use: [
                              // use数组执行顺序:从下到上
                              {
                                loader: MiniCssExtractPlugin.loader // 从js文件中找到css字符串,提取css成单独css文件
                              },
                              {
                                loader: "css-loader" // 将 CSS 转化成 字符串,会以 CommonJS 模块化整合js文件中
                              },
                              {
                                loader: "less-loader" // 将 Less 编译成 CSS
                              }
                            ]
                          },
                          {
                            // 处理图片文件(默认是不能处理html的图片,让html加载图片)
                            test: /\.(png|jpg|gif)$/,
                            loader: "url-loader",
                            options: {
                              /*
                                11kb以下的图片会被base64处理 
                                        优点:图片不会发送额外的请求,随着html文件一起被请求下来(减少服务器压力)
                                        缺点:体积会变的更大
                                        所以一般针对小图片来做
                              */
                              limit: 11000,
                              // [hash:10] hash值取10位
                              // [ext] 原来文件扩展名是啥就是啥
                              name: "static/media/[hash:10].[ext]",
                              // 关闭ES6模块化,使用Commonjs模块化
                              // 解决 html 中 img src 为 [object Module]
                              esModule: false
                            }
                          },
                          {// 解决 html 中 img src 为 解析前路径的问题,找不到资源
                            test: /\.(html)$/,
                            loader: "html-loader"
                          },
                          {
                            // 排除文件
                            // 解决其它资源,如iconfont
                            exclude: /\.(less|jpg|png|gif|js|html)$/,
                            loader: "file-loader",
                            options: {
                              name: "static/media/[hash:10].[ext]"
                            }
                          }
                        ]
                      },
                      // plugins
                      plugins: [
                        new HtmlWebpackPlugin({
                          // 以 './src/index.html' 为模板创建新的html文件
                          // 新html文件结构和原来一样 并且 会自动引入webpack打包生成的js/css资源
                          template: "./src/index.html",
                          // 压缩html文件选项
                          minify: {
                            collapseWhitespace: true, // 去除换行符/空格
                            removeComments: true, // 去除注释
                            removeRedundantAttributes: true, // 去除默认值标签属性
                            removeScriptTypeAttributes: true, // 删除script type
                            removeStyleLinkTypeAttributes: true, // 删除link type
                            useShortDoctype: true // 使用短的doctype(html5)
                          }
                        }),
                        //将css文件从js里面分离出来[name].css代码分离出来的名字为main.js
                        new MiniCssExtractPlugin({
                          filename: "static/css/build.css"
                        }),
                        // 压缩css的插件
                        new OptimizeCssAssetsPlugin({
                          cssProcessorPluginOptions: {
                            preset: ["default", { discardComments: { removeAll: true } }]
                          }
                        }),
                        // 自动删除由于重命名在打包的重复文件
                        new CleanWebpackPlugin()
                      ],
                      // mode
                      mode: "production" // 生产环境,会自动压缩js,而html和css需要配置才能压缩
                    };

                5.运行指令
                    webpack.dev.js:配置开发环境,运行指令npm start(会在内存中生成输出目录build,不会再本地生成输出目录build)
                    webpack.prod.js:配置生产环境,运行指令npm run build,在运行指令serve build(会再本地生成输出目录build)
                    
                    注意:除了start,其余指令都得加 run,本地下载的有些包会将运行的脚本添加到 node_modules/.bin 目录中
                        在start指令运行时,会将node_modules/.bin临时添加为环境变量
                        run的作用就是将node_modules/.bin临时添加为环境变量
                6.常见错误(错误一般只分析前面几行)
                    1)`Module not found: Error: Can't resolve 'style-loader' in 'C:\Users\XiongJian\Desktop\class191108\05.项目构建\02.webpack'`
                        style-loader 模块没有找到
                              下载安装:npm i style-loader -D
                    2)`Error: Cannot find module 'less'`
                        less 模块没有找到
                              下载安装:npm i less -D
                    3)`xxx is not define`
                              说明 xxx 没有定义
                              定义或者引入
                    4)生产环境路径配置
                        - 问题:
                          - css 文件和 html 文件不在同一个目录
                          - css 文件和 html 文件中的图片都是被 url-loader 处理
                          - 所以两个文件的图片路径必须是一样的
                          - 所以导致至少有一个文件图片路径出现问题
                        
                        - 解决
                          - 用 / 路径 替换 ./ 相对路径
                          - / 路径是以根目录出发,不管你在哪个文件夹,都以根目录为起点就不会有问题
                          - 不能本地运行,需要开启服务器去服务
                          - npm i serve -g 
                          - serve build  以build为根目录启动一个服务器
                          
2.webpack面试精讲
    1)包功能
        webpack        具备webpack基本功能
        webpack-dev-server    配置自动化devServer
        webpack-cli        运行packahe.json中配置的指令,如运行自动化指令
        html-webpack-plugin        打包html
        style-loader css-loader        处理样式,如果用less写样式,只需将css-loader改为less-loader
        mini-css-extract-plugin        将css文件从js文件单独抽取出来
        @babel/core babel-loader @babel/preset-env        处理js兼容性,@babel/preset-env只能将一些普通ES6语法转换ES5一下,一旦一些较复杂的语法,如Promise、Async等,就不会转化(此时在IE浏览器运行就报错)
        @babel/polyfill    core-js        @babel/polyfill可以处理所有ES6以上的语法JS兼容包(不包含基本ES6语法),但是没有按需加载功能,需要配合core-js才有按需加载功能
        postcss-loader postcss-import postcss-preset-env cssnano    处理css兼容
        clean-webpack-plugin    自动删除上一个打包的结果
        workbox-webpack-plugin    使用PWA插件
    2)配置自动化
        主要配置主机名、端口号、是否开启压缩、是否自动打开浏览器等功能
        //devServer与五大核心模块同级
        devServer: {
            contentBase: path.resolve(__dirname, 'public'),     //运行(构建后)代码的根目录
            open: true, // 自动打开浏览器
            host: 'localhost',
            port: 9527,
            compress: true, // 启动gzip压缩资源
        }
    3)HMR(hot module replacement 热模块替换)
        配置自动化存在热更新(默认情况,改动一个文件,会全部刷新,会全部重新打包性能不好),HMR可以解决这个问题
        改动一个文件,只更新这一个文件,其他文件不变
        plugins: [
            // 添加支持HMR功能的插件
            new webpack.HotModuleReplacementPlugin()
        ],
        devServer: {
            hot: true, // 启动HMR功能
        }
        上面处理只能让样式文件有HMR功能(style-loader内部处理了),但是,JS文件没有HMR功能,想让js文件有HMR功能需要在js主文件中做如下处理
        // 判断当前有没有启动HMR功能
        if (module.hot) {
          // add.js开启HMR功能
          module.hot.accept("./add", () => {
            // 一旦add.js文件发生变化,就会重新加载这个文件,其他文件就不会变化
            // 最后执行当前函数
            console.log("当前函数执行了~");
          });
        }
    4)兼容性(js/css)
        1.polyfill(处理JS)
            @babel/preset-env只能将一些普通ES6语法转换ES5一下,一旦一些较复杂的语法,如Promise、Async等,就不会转化(此时在IE浏览器运行就报错)
            @babel/polyfill可以处理所有ES6以上的语法JS兼容包(不包含基本ES6语法),但是没有按需加载功能,需要配合core-js才有按需加载功能
            {
                test: /\.js$/, //匹配js文件
                include: path.resolve(__dirname, "src"),
                loader: "babel-loader",
                options: {
                  presets: [//预设
                    [
                      "@babel/preset-env", 
                      {
                        useBuiltIns: 'usage', //按需加载
                        // 使用兼容性的包,指定版本
                        corejs: {
                          version: 3
                        },
                        // 需要覆盖99.5%的浏览器,但是不要死了的
                        targets: '>0.5%, not dead',
                        // 不要将ES6模块化装化成COMMONJS(cjs)
                        modules: false
                      }
                    ]
                  ],
                },
              }
        2.postcss(处理css)
            设置 process.env.NODE_ENV = "development" / "production";
            {
              loader: "postcss-loader",
              options: {
                ident: "postcss",
                plugins: (loader) => [
                  require("postcss-import")({ root: loader.resourcePath }),
                  require("postcss-preset-env")(),
                  require("cssnano")(),
                ],
              },
            },
            默认情况下就能做一些css兼容性处理了,但是不够全
            所以,还需要再package.json中配置
              文档:https://github.com/browserslist/browserslist
              "browserslist": {
                "development": [ 
                  "last 1 Chrome versions"
                ],
                "production": [
                  "cover 99.5%",
                  "not dead",
                  "not op_mini all"
                ]
              }            
    5)tree shaking 树摇(去除一些引入没有使用的代码)
        如何使用tree shaking:
              1. 使用ES6模块化
              2. 使用webpack production环境
              3. 注意使用babel时,@babel/preset-env会自动转化ES6模块化成cjs,所有要进行配置不能讲ES6模块转成cjs模块
                      所以不能让其@babel/preset-env转化
                      所以有如下配置:
                        // 不要将ES6模块化装化成COMMONJS(cjs)
                        modules: false
              4. 默认只会对js文件使用tree shaking,想要让其它文件使用tree shaking需要在package.json中配置
                    问题:css文件使用tree shaking,由于css样式的引入方式webpack会误认为样式没有使用,会把样式文件删除
                    解决:
                          在package.js文件中配置不需要tree shaking的文件
                          "sideEffects": [
                           "*.css", // 样式文件是有副作用(不能进行tree shaking)
                           "@babel/polyfill"
                         ]  
    6)代码分割code split
        1.入口文件多个,输出文件多个,这样页面需要加载那个文件就会加载那个文件,存在问题是:多个文件中公共的代码不会被抽取成单独文件,可以进一步优化
        2.optimization可以解决将输出的多个文件中公共代码抽取成单独文件
            功能:
              1) 将入口JS文件中node_modules中大于30KB的打包到单独模块vendors
              2) 将一个大于30KB且至少在两个入口分别引入了一次的模块打包到单独模块defaults
                  配置(webpack中配置):
                      //optimization与五大核心模块同级
                      optimization: {
                        splitChunks: {
                          chunks: "all"
                        }
                      }
            问题:开发时一般都是单入口
              1) 将入口JS文件中node_modules中大于30KB的打包到单独模块vendors(没有问题)
              2) 将一个大于30KB且至少在两个入口分别引入了一次的模块打包到单独模块defaults(有问题就是只有单入口不可能被引入两次,这个配置就不可能生效)
                  最终结果:只能打包node_modules代码到单独文件
                  但是其他代码还是汇总到一个文件中,代码体积仍然很大
        3.懒加载
            解决optimization遗留下来的问题,如果你想代码被单独打包成一个文件,将来单独加载,需要使用动态导入
            在Vue中,
              const Foo = () => import('./Foo.vue')
              const router = new VueRouter({
                routes: [
                  { path: '/foo', component: Foo }
                ]
              })
    7)cache缓存
        1. 文件命名 [name].js
                问题:一旦文件被强制缓存起来,在强制缓存期间内,文件内容改变,浏览器页面不会发生改变,因为默认自动走缓存
           2. 文件命名 [name].[hash:8].js(生成文件名时含有带8位的hash值)
            让文件名不一样,就不会使用上次文件的强制缓存了
            问题:当你改动样式/js文件,会导致其他没有变化的文件缓存失效
            原因:所有文件共享一个hash值,一旦文件发生变化,就会全部修改
                  只要webpack重新打包,即使文件没有变,也会生成新的hash值,导致所有缓存失效
            hash: webpack每次打包都会生成一个唯一的hash值
        3. 文件命名 [name].[contenthash:8].js  
          contenthash 根据文件内容来生成contenthash,只要文件内容不一样,hash一定不一样
            问题:当你使用动态导入语法对math.js进行代码分割
              一旦修改math.js文件,会导致index.js缓存失效
            原因:index.js中会记录math.js的打包chunkId(contenthash生成的) 
              当math.js文件发生变化(contenthash会改变),
              导致index.js中的math.js的chunkId发生变化,从而导致index.js也变化了(contenthash会改变)
        4. 将所有JS文件记录ID的内容,提取成单独文件runtime.xxx.js
            配置:
              runtimeChunk: {
                name: (entryPoint) => `runtime-${entryPoint.name}`
              }
            当math.js文件发生变化(contenthash会改变),只会导致 runtime.js 文件也发生变化
            而index.js不变
    8)缓存babel和eslint
        缓存js的babel和eslint,提高webpack再次打包的时间,缓存其它文件的打包可以使用cacahe-loder插件设置
    9)PWA缓存    
        使用service worker+cache Storage技术,离线缓存之前浏览过的记录
        // 主入口文件中注册 servise worker
        if ("serviceWorker" in navigator) {
          window.addEventListener("load", () => {
            navigator.serviceWorker
              .register("/service-worker.js")
              .then((registration) => {
                console.log("SW registered: ", registration);
              })
              .catch((registrationError) => {
                console.log("SW registration failed: ", registrationError);
              });
          });
        }
    10)oneof
        默认情况下,一个模块要被所有loader都过一遍,这样性能稍差,实际上我们只需要其中某个loader处理,其他loader就不用看了
        一个文件如果需要多个loader处理,那么就把Loder放在oneof数组外面
        oneOf: [
          {
            test: /\.css$/,
            include: path.resolve(__dirname, "src"),
            use: [MiniCssExtractPlugin.loader, "css-loader"],
            sideEffects: true, // 当前处理的文件都有副作用,不要进行tree shaking
          }
        ]
    11)devtool
        1. 问题
            webpack会将所有代码打包成一个文件,一旦文件出错,提示的是打包后文件的错误,
            我们没法通过打包后代码观察源代码到底有什么问题(不好调试错误)          
        2. 解决 source-map
            source-map 是一个技术,最终会有一个文件 xxx.map
            提供一个源代码与webpack构建后代码的一一映射关系
            当构建后代码出现问题,就可以根据source-map文件,追踪到源代码出现的问题,
            从而提示源代码的错误,这样就能方便开发者调试错误          
        3. 使用
            devServer: {
                devtool: 'source-map'  
            }
            开发环境: 为了让首次构建和重新构建速度更快
              cheap-module-eval-source-map
            生产环境: 为了让调试更友好,打包体积更小
              source-map
              
3.总结 Webpack
    1)Webpack 基本概念
        1. entry 入口        
        - 以某个文件为入口开始打包
        - 分类
          - 单入口 String 
            - 只会输出一个文件
            - 单页面应用(SPA)
          - 多入口 Array / Object
            - Array 只会输出一个文件
            - Object 会输出多个文件
              - 多页面应用        
        2. output 输出        
        - 打包后资源输出到哪里去        
        3. loader 加载器        
        - webpack 本身只能识别 json、js 模块,其他模块一旦加载就会报错
        - 需要借助 loader 帮助 webpack 识别它识别不了的模块        
        4. plugins 插件        
        - loader 功能有限,要想做功能更加强大的工作交给插件        
        5. mode        
        - 模式:开发环境(development)和生产环境(production)    
    2)Webpack 基本配置
        1. 处理JS文件
          eslint-loader
            在package.json中配置eslintConfig来指示eslint-loader到底要干什么事
            enfore: 'pre' 优先执行
          babel-loader
            在package.json中(webpack配置文件中也行)配置babel来指示babel-loader到底要干什么事
        2. 处理Vue文件
          vue-loader(语法)
        3. 处理JSX文件
          babel-loader
            presets: ['@babel/preset-react']
        4. 处理CSS文件
          开发环境:创建style标签插入样式
            style-loader(创建style标签插入样式)
            css-loader/less-loader / sass-loader / stylus-loader(解析样式)
            postcss-loader(css兼容性)
          生产环境:提取单独css文件,将来通过link引入
              // 从js文件中单独提取css文件
            MiniCssExtractPlugin.loader(loader后面是插件)(还需要配置插件(抽取出来的文件名称) new MiniCssExtractPlugin)
            OptimizeCssAssetsPlugin        压缩css文件
            css-loader/less-loader / sass-loader / stylus-loader
            postcss-loader
        5. 处理HTML文件
          // 以 './src/index.html' 为模板创建新的html文件
          // 新html文件结构和原来一样 并且 会自动引入webpack打包生成的js/css资源
          // 还可以进行html文件压缩配置
          new HtmlWebpackPlugin({ template: './public/index.html' })
          html-loader    解决 html 中 img src 为 解析前路径的问题,找不到资源
        6. 处理图片文件
          url-loader
            limit: 10000 小于10kb一下的图片会被base64处理
        7. 处理其他类型文件(如字体图标、音视频等)
          file-loader 原封不动输出
        8.补充
            CleanWebpackPlugin    // 自动删除由于重命名在打包的重复文件
    
    3)Webpack 优化手段
        1.优化打包构建速度
            1) HMR 热模块替换
              为什么要用?
                默认情况下,一旦修改了代码,全部代码重新编译刷新,速度慢(全体刷新)
              有什么作用?
                只更新修改的模块,其他模块不变(局部更新)  
              怎么使用?
                devServer: { hot: true }  
                new webpack.HotModuleReplacementPlugin()  
                注意:默认情况下只有样式文件有HMR功能(style-loader),JS是没有的
                开启JS的HMR功能:
                  1. 手写JS代码 --> module.hot.accpet('模块路径', () => {})
                  2. 在Vue使用 --> vue-loader
                  3. 在React使用 --> react-hot-loader
            
            2) cache 缓存(针对js)
              eslint和babel两个任务处理JS文件,时间一般会比较长,为了让其重新构建速度更快
              可以使用缓存。
                eslint --> cache: true
                babel --> cacheDirectory: true
              
              cache-loader(可以缓存其它文件)放置在要缓存loader的后面
              注意:一般只针对耗时长的任务:eslint-loader/babel-loader/vue-loader
            
            3) oneOf
              作用:让模块只被一个loader处理,其他的就不看了(默认情况会被所有的loader处理)~
                  能够提升打包速度~,如果一个文件需要被两个loader处理,那么可以一个放在oneof里面一个放在外面
              注意:eslint-loader(外) / babel-loader(内)              
            4)多进程打包
              过去(2019前): happyPack
              现在: thread-loader
              用法和cache-loader差不多,放在要使用loader后面执行
              作用:开启多进程处理前面的任务,提升打包速度
              注意:每个进程开启(600毫秒)和通信都有开销,一般只针对耗时长的任务:babel-loader
            
        2.优化打包代码体积和性能
            1) 兼容性处理
              JS
                1. babel-loader   配置智能处理presets: ['@babel/preset-env'] 问题就是只能编译简单js语法
                2. @babel/polyfill 做复杂语法(如promise或async)兼容,问题是使用按需引入体积太大了
                3. core-js 在@babel/preset-env基础上,增加了useBuiltIns: 'usage'来实现按需加载(1和3就可以,不用2)
              CSS  
                postcss-loader 
                在package.json中指定browserslist来指示postcss-loader兼容性做到什么程度            
            2) tree shaking 树摇
              去除没有使用的JS代码
              注意:
                1. 必须使用ES6模块化(需要禁止@babel/preset-env转换ES6模块化语法 modules: false)
                2. 开启webpack的生产模式(内部启用TerserPlugin,用来压缩JS代码的插件,tree shaking功能就是这个插件完成的)
                3. 在package.json配置sideEffects来指定哪些文件需要进行tree shaking(有些文件会有副作用,如css文件会被默认剔除掉)            
            3) code split 代码分割 / lazy loading 懒加载
              作用:1. 抽取公共代码 2. 拆分多个文件,减少单个文件体积(避免单次请求时间过长)
              配置:
                1. 多入口 + optimization
                      默认将超过30kb的node_modules弄快抽取成单独模块
                       默认将超过30kb多入口的公共模块也抽取成单独模块(现实中只会有一个入口文件,所以下面一种的使用方式较多)
                2. 单入口 + optimization(webpack文件中的一个配置项) + import
                      将node_modules抽取成单独模块
                      动态导入语法import就能将某些文件抽取成单独模块
                    1. 原生 JS
                    2. React,可以使用Suspence + lazy(套在组件外面)来实现路由组件的动态导入
                    3. Vue,() => import('./Foo.vue')
                      React和Vue的目的都是为了将路由组件抽取成单独模块,单独加载(懒加载)        
            4) preload 和 prefetch 预加载
              作用:让资源提前加载
              区别:
                preload 让当前页面的将要使用资源提前加载
                prefetch 让后面页面要使用资源提前加载(当前不需要使用)
              使用:
                  // 导入模块前加注释
                import(/* webpackPreload: true */'./xxx')
                import(/* webpackPrefetch: true */'./xxx')
              问题:兼容性较差                
            5) cache 缓存(浏览器缓存)
              1. hash 
                webpack每次构建都会生成一个新的且唯一的hash
                问题:只要webpack重新构建,所有文件的hash都会发生变化,缓存就会失效
              2. chunkhash
                打包属于同一个chunk(父子关系),就共享同一个hash
                问题:样式文件被css-loader打包js文件中,导致样式文件和js文件属于同一个chunk,共享同一个hash
                一旦样式文件发生变化,js文件也会变
              3. contenthash
                根据文件的内容来成hash,所以只要文件内容不一样,hash就不一样
                问题:比如A模块有一个依赖,是B模块,那么A模块内部就会保存B模块hash值,
                  一旦B模块发生变化,会导致AB模块的hash都会发生改变
                解决:runtimechunk: true 将A模块保存的B模块hash值存到runtime文件中,这样B模块的修改只会改变runtime文件
            
              最终:就能让资源进行持久缓存            
            6) PWA 渐进式网络应用程序(离线加载技术)
              作用:让我们开发的App,即使离线也可以访问
              内部使用 service worker + cache 实现的
              具体实现我们借助插件 workbox-webpack-plugin             
    4)Webpack 原理分析(后面自己学习)
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值