• 第一章、webpack高级


    webpack基础打包

    1、webpack的安装
    • webpack使用前提
    webpack依赖node环境
    
    • webpack的安装
    # 1、初始化项目(项目名不能包含中文)
    npm init -y
    # 2、安装webpack、webpack-cli
    npm i webpack webpack-cli -D
    
    • webpack的打包
    * webpack:全局命令
    * npx webpack:node_modules命令
    * package.json -> "scripts" -> "build": "webpack":node_modules命令
    
    2、webpack的配置文件
    • 默认配置文件名(package.json)
    "scripts": {
        "build": "webpack --config webpack.config.js"
    }
    
    • 默认配置(webpack.config.js)
    // path模块:由node环境提供,package.json中无需依赖
    const path = require('path');
    
    module.exports = {
        // 打包的入口文件名
        entry: './src/index.js',
        output: {
            /**
             * path.resolve():合并路径
             * __dirname:由node环境提供,当前配置文件所在路径
             * ./dist:打包的输出路径
             */
            path: path.resolve(__dirname, './dist'),
            // 打包的出口文件名
            filename: 'main.js'
        }
    };
    
    3、webpack的依赖图
    * webpack打包过程
          - 根据配置文件找到入口文件
          - 从入口文件开始,找到所有依赖的文件,形成一个依赖关系图(一种数据结构:图结构)
          - 遍历图结构,打包一个一个文件(根据不同的文件类型,使用不同的loader解析)
    * --save-dev和--save使用错误,打包不会有问题,为了规范尽量不要使用错误
    
    4、loader的使用(以css-loader为例)
    • loader的作用
    webpack并不知道如何对css类型的文件进行加载,需通过对应的loader进行转换。
    
    • loader的安装
    npm i -D css-loader style-loader
    
    • loader的使用方案
    * 内联方式(import "css-loader!@/styles/index.css")
    * CLI方式(webpack5中不再使用)
    * 配置方式
    
    • 配置方式使用loader(webpack.config.js)
    module.exports = {
        module: {
            rules: [
                {
                    test: /\.css$/,// 正则表达式用以匹配文件
                    // 1、loader写法(语法糖)
                    // loader: "css-loader"
                    // 2、完整写法
                    use: [
                        // loader是有加载顺序的(从后往前)
                        {loader: "style-loader", options: {}},
                        {loader: "css-loader", options: {}}
                    ]
                }
            ]
        }
    };
    
    5、webpack+sass
    • 安装
    npm i -D sass sass-loader
    
    • 配置(webpack.config.js)
    module.exports = {
        module: {
            rules: [
                {
                    test: /\.scss$/,
                    use: ["style-loader", "css-loader", "sass-loader"]
                }
            ]
        }
    };
    
    6、postcss
    • 安装
    # 命令:npx postcss --use autoprefixer -o entry.css output.css
    # npm i postcss postcss-cli -D
    npm i postcss-loader -D
    # 补全前缀
    # npm i autoprefixer -D
    # 转换一些现代的css特性(比如:#ffffffff),并补全前缀
    npm i postcss-preset-env -D
    
    • 配置(webpack.config.js)
    module.exports = {
        module: {
            rules: [
                {
                    test: /\.css$/,
                    use: [
                        "style-loader",
                        "css-loader",
                        {
                            loader: "postcss-loader",
                            // 配置方式一
                            /*options: {
                                postcssOptions: {
                                    plugins: [
                                        require("autoprefixer")
                                    ]
                                }
                            }*/
                        }
                    ]
                }
            ]
        }
    };
    
    • 配置(postcss.config.js)
    module.exports = {
        plugins: [
            require("postcss-preset-env")
        ]
    }
    
    7、file-loader(webpack5中测试失败)
    • 安装
    npm i -D file-loader
    
    • 配置(webpack.config.js)
    module.exports = {
        module: {
            rules: [
                {
                    test: /\.(jpe?g|png|gif|svg)$/i,
                    use: [{
                        loader: "file-loader",
                        options: {
                            // outPath: "img",
                            name: "img/[name]_[hash:6].[ext]"
                        }
                    }]
                }
            ]
        }
    };
    
    • 文件的命名规则
    * [ext]:处理文件的扩展名
    * [name]:处理文件的名称
    * [hash]:文件的内容,使用MD4的散列函数处理,生成的一个128位的hash值(32个十六进制)
    * [contentHash]:在file-loader中和[hash]结果是一致的(在webpack的一些其他地方不一样)
    * [hash:<length>]:截取hash的长度,默认32个字符太长了
    * [path]:文件相对于webpack配置文件的路径
    
    8、url-loader(webpack5中测试失败)
    • 安装
    npm i -D url-loader
    
    • 配置(webpack.config.js)
    module.exports = {
        module: {
            rules: [
                {
                    test: /\.(jpe?g|png|gif|svg)$/i,
                    use: [{
                        loader: "url-loader",
                        options: {
                            name: "img/[name]_[hash:6].[ext]",
                            limit: 1024 * 100 // KB
                        }
                    }]
                }
            ]
        }
    };
    
    9、资源模块类型(asset module type)
    • 配置(webpack.config.js)
    module.exports = {
        entry: './src/index.js',
        output: {
            path: path.resolve(__dirname, './dist'),
            filename: 'js/main.js',
            // assetModuleFilename: "img/[name]_[hash:6][ext]"
        },
        module: {
            rules: [
                {
                    test: /\.(jpe?g|png|gif|svg)$/i,
                    type: "asset",
                    generator: {
                        filename: "img/[name]_[hash:6][ext]"
                    },
                    parser: {
                        dataUrlCondition: {
                            maxSize: 1024 * 100
                        }
                    }
                }
            ]
        }
    };
    
    • 4种模块类型(替代file-loader、url-loader)
    * asset/resource:发送一个单独的文件并导出URL。之前通过使用file-loader实现
    * asset/inline:导出一个资源的data URI。之前通过使用url-loader实现
    * asset/source:导出资源的源代码。之前通过使用raw-loader实现
    * asset:在导出一个data URI和发送一个单独的文件之间自动选择。之前通过使用url-loader,
      并且配置资源体积限制实现
    
    10、加载字体文件(webpack.config.js)
    module.exports = {
        module: {
            rules: [
                // file-loader测试失败
                /*{
                    test: /\.(eot|ttf|woff2?)$/,
                    use: {
                        loader: "file-loader",
                        options: {
                            name: "font/[name]_[hash:6].[ext]"
                        }
                    }
                },*/
                {
                    test: /\.(eot|ttf|woff2?)$/,
                    type: "asset/resource",
                    generator: {
                        filename: "font/[name]_[hash:6][ext]"
                    }
                }
            ]
        }
    };
    
    11、认识Plugin
    * Loader是用于特定的模块类型进行转换
    * Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等
    
    12、CleanWebpackPlugin
    • 安装
    npm i -D clean-webpack-plugin
    
    • 配置(webpack.config.js)
    const {CleanWebpackPlugin} = require("clean-webpack-plugin")
    
    module.exports = {
        plugins: [
            new CleanWebpackPlugin()
        ]
    };
    
    13、HtmlWebpackPlugin和DefinePlugin
    • 安装
    npm i -D html-webpack-plugin
    # DefinePlugin是webpack内置插件,无需安装
    
    • 配置(webpack.config.js)
    const HtmlWebpackPlugin = require("html-webpack-plugin")
    const {DefinePlugin} = require("webpack")
    
    module.exports = {
        plugins: [
            new HtmlWebpackPlugin({
                template: "./public/index.html",
                title: "孟美岐" // htmlWebpackPlugin.options.title
            }),
            new DefinePlugin({
                BASE_URL: "'./'" // 允许在编译时创建配置的全局常量
            })
        ]
    };
    
    14、CopyWebpackPlugin
    • 安装
    npm i -D copy-webpack-plugin
    
    • 配置(webpack.config.js)
    const CopyWebpackPlugin = require("copy-webpack-plugin")
    
    module.exports = {
        plugins: [
            new CopyWebpackPlugin({
                patterns: [{
                    from: "public",
                    to: "./",
                    globOptions: {
                        ignore: ["**/index.html"]
                    }
                }]
            })
        ]
    };
    
    15、mode和devtool(webpack.config.js)
    module.exports = {
        /**
         * 设置模式:
         *     - development:开发阶段,会设置development
         *     - production:准备打包上线的时候,设置production
         */
        mode: "development",
        // 设置source-map,建立js映射文件,方便调试代码和错误
        devtool: "source-map"
    };
    
    16、babel
    • 安装
    ##################
    # 命令:npx babel index.js --out-dir dist --out-file main.js 
    #      --plugins=@babel/plugin-transform-arrow-functions,
    #                @babel/plugin-transform-block-scoping
    ##################
    # npm i -D @babel/core @babel/cli
    # 转换箭头函数
    # npm i -D @babel/plugin-transform-arrow-functions
    # 转换块级作用域
    # npm i -D @babel/plugin-transform-block-scoping
    ##################
    # babel的预设
    # 命令:npx babel index.js --out-file main.js --presets=@babel/preset-env
    ##################
    npm i -D @babel/preset-env
    # @babel/core也要安装(测试结果并不需要)
    npm i -D babel-loader
    
    • babel的底层原理
    * babel可以看成是一个编译器
    * 简化版的编译器工作流程
          - 原生源代码
          - 解析(parsing)
          - 转换(transformation)
          - 代码生成(code generation)
          - 目标源代码
    * 每个阶段具体的工作
          - 原生源代码
          - 词法分析(lexical analysis)
          - tokens数组
          - 语法分析(syntactic analysis)(也称为parsing)
          - AST抽象语法树
          - 遍历(traversal)
          - 访问(visitor)
          - 应用插件(plugin)
          - 新的AST(新的抽象语法树)
          - 目标源代码
    
    • 配置(webpack.config.js)
    module.exports = {
        module: {
            rules: [
                {
                    test: /\.js$/,
                    use: {
                        loader: "babel-loader",
                        // 配置方式一
                        /*options: {
                            /!*plugins: [
                                "@babel/plugin-transform-arrow-functions",
                                "@babel/plugin-transform-block-scoping"
                            ],*!/
                            presets: [
                                ["@babel/preset-env", {}]
                            ]
                        }*/
                    }
                }
            ]
        }
    };
    
    • 配置(babel.config.js)
    /**
     * babel.config.js(或者.json、.cjs、.mjs)文件(monorepos项目)(babel7推荐)
     * .babelrc.js(或者.babelrc、.json、.cjs、.mjs)文件(rc:runtime compiler)
     */
    module.exports = {
        presets: [
            "@babel/preset-env"
        ]
    }
    
    17、vue源码的打包
    • 安装
    # vue3需要加@next
    npm i -S vue@next
    ##################
    # 配置(webpack.config.js)
    # const {DefinePlugin} = require("webpack")
    # const {VueLoaderPlugin} = require("vue-loader/dist/index")
    # 
    # module.exports = {
    #     module: {
    #         rules: [
    #             {
    #                 test: /\.vue$/,
    #                 loader: "vue-loader"
    #             }
    #         ]
    #     },
    #     plugins: [
    #         new DefinePlugin({
    #             // 配置用以消除警告
    #             __VUE_OPTIONS_API__: true,
    #             __VUE_PROD_DEVTOOLS__: false,
    #         }),
    #         new VueLoaderPlugin()
    #     ]
    # };
    ################## 
    npm i -D vue-loader@next
    # 测试结果并不需要
    # npm i -D @vue/compiler-sfc
    
    • vue打包后不同版本解析
    * vue(.runtime).global(.prod).js
          - 通过浏览器中的<script src="...">直接使用
          - 我们之前通过CDN引入和下载的vue版本就是这个版本
          - 会暴露一个全局的Vue来使用
    * vue(.runtime).esm-browser(.prod).js
          - 用于通过原生ES模块导入使用(在浏览器中通过<script type="module">来使用)
    * vue(.runtime).esm-bundler.js
          - 用于webpack、rollup和parcel等构建工具
          - 构建工具中默认是vue.runtime.esm-bundler.js
          - 如果我们需要解析模板template,那么需要手动指定vue.esm-bundler.js
    * vue.cjs(.prod).js
          - 服务端渲染使用
          - 通过require()在node.js中使用
    
    • 运行时+编译器 vs 仅运行时
    * 在Vue的开发过程中我们有三种方式来编写DOM元素
          - template模板的方式(需要通过源码中compiler部分代码来进行编译)
          - render函数的方式,使用h函数来编写渲染的内容(h函数可以直接返回一个虚拟节点,
            也就是VNode节点)
          - 通过.vue文件中的template来编写模板(通过vue-loader对其进行编译和处理)
    * 仅运行时不包含对template模板的编译代码,所以相对运行时+编译器更小一些
    
    • 运行时+编译器版本
    import {createApp} from "vue/dist/vue.esm-bundler.js"
    
    /**
     * index.html文件部分代码
     * <template id="my-app">
     *     <h3>{{text}}</h3>
     * </template>
     */
    const app = createApp({
        template: `#my-app`,
        components: {},
        data() {
            return {
                text: "这是一段中文"
            }
        }
    })
    app.mount("#app")
    
    • webpack对SFC文件的支持
    import {createApp} from "vue"
    import App from "./App.vue"// 后缀名不能省略
    
    /*
    // App.vue文件代码
    <template>
    <h3>{{text}}</h3>
    </template>
    
    <script>
    export default {
        name: "App",
        data() {
            return {
                text: "这是一段中文"
            }
        }
    }
    </script>
    
    <style scoped>
    h3 {
        color: red;
    }
    </style>
    */
    const app = createApp(App)
    app.mount("#app")
    
    18、vue搭建本地服务器
    • webpack提供完成自动编译的几种方式
    - webpack watch mode
    - webpack-dev-server(常用)
    - webpack-dev-middleware
    
    • webpack watch
    * 该模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译
    * 两种方式开启watch:
    
    // 方式一、package.json
    {
      "scripts": {
        "build": "webpack --watch"
      }
    }
    
    // 方式二、webpack.config.js
    module.exports = {
        watch: true,
    }
    
    • webpack-dev-server
    * 安装:npm i -D webpack-dev-server
    
    // 配置(package.json)
    {
      "scripts": {
        "serve": "webpack serve",
      }
    }
    
    // 开发阶段静态资源配置(webpack.config.js)(测试结果此配置已不再生效)
    module.exports = {
        devServer: {
            // 本地服务找不到的资源,从此配置的目录中找
            contentBase: "./public"
        }
    }
    
    • webpack-dev-server流程解析
    * webpack-dev-server不需与webpack watch一起使用
    * webpack-dev-server不会写入到任何输出文件,而是将文件保留在内存中
      (使用了一个库叫memfs。memory-fs是webpack自己写的,目前已经停更)
    * 源代码 -> 通过webpackDevServer打包至内存(使用memfs库)
      -> 本地服务(express框架)从内存中获取资源 -> 浏览器访问
    
    • 模块热替换(HMR)
    * 全称Hot Module Replacement,是指在应用程序运行过程中,替换、添加、删除模块,
      而无需重新刷新整个页面。
    * 社区成熟的解决方案
          - vue开发中:vue-loader已经支持了.vue文件HMR
          - react开发中:react hot loader(目前已弃用,改用react-refresh)
    
    // 配置(webpack.config.js)
    module.exports = {
        target: "web",// node项目值为:"node"
        devServer: {
            hot: true // 默认就是true
        },
    }
    
    // 操作模块成为热更新模块
    import "./utils/index"
    
    if (module.hot) {
        module.hot.accept("./utils/index.js", () => {
            console.log("发生了热更新")
        })
    }
    
    • HMR的原理
    * webpack-dev-server会创建两个服务:
          - 提供静态资源的服务(express)
          - Socket服务(net.Socket)
    * HMR Socket Server,是一个socket的长连接
          - 长连接有一个最好的好处是建立连接后双方可以通信(服务器可以直接发送文件到客户端)
          - 当服务器监听到对应的模块发生变化时,
            会生成两个文件.json(manifest文件)和.js文件(update chunk)
          - 通过长连接,可以直接将这两个文件主动发送给客户端(浏览器)
          - 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,
            并且针对修改的模块进行更新
    
    • devServer的其它配置
    module.exports = {
        devServer: {
            /**
             * localhost(127.0.0.1)回环地址,不经过数据链路层和物理层,其他主机无法访问。
             * 如果想要其他主机可以访问,设值为:"0.0.0.0"
             */
            host: "localhost",
            port: "8080", // 端口号
            /**
             * 打开浏览器,也可以在package.json中配置:
             * {"script":{"serve":"webpack serve --open"}}
             */
            open: true,
            // 是否开启gzip压缩。浏览器默认支持gzip,会自动解压
            // compress: false
        },
    }
    
    • Proxy跨域代理
    module.exports = {
        devServer: {
            proxy: {
                '/api': {//拦截请求,此时url:/api/requesturl
                    // 在请求前加源地址,此时url:http://www.baidu.com:8080/api/requesturl
                    target: 'http://www.baidu.com:8080',
                    pathRewrite: {
                        // 重写请求,此时url:http://www.baidu.com:8080/requesturl
                        '^/api': ''
                    },
                    /**
                     * 修改request headers的host为target的值,
                     * 需后端通过request.getHeader("Host")方式查看
                     */
                    changeOrigin: true, 
                    secure: false // 默认true,不支持接收转发到https的服务器上
                }
            }
        },
    }
    
    • resolve模块解析
    module.exports = {
        resolve: {
            modules: ["node_modules"],// 默认查找模块的路径
            mainFiles: ["idnex"],// 默认查找文件名
            extensions: [".wasm", ".mjs", ".js", ".json"],// 默认查找文件后缀名
            alias: {// 路径别名
                "@": path.resolve(__dirname, "./src")
            }
        },
    }
    
    19、webpack-merge
    • 安装
    npm i -D webpack-merge
    
    • 配置(webpack(.prod|.dev).config.json)
    const {merge} = require("webpack-merge")
    
    module.exports = merge(otherConfig(导入的其它配置),{}(当前要做的配置))
    
    • 配置(package.json)
    {
      "scripts": {
        "serve": "webpack serve --config ./webpack.dev.config.js",
        "build": "webpack --config ./webpack.prod.config.js"
      },
    }
    
  • 相关阅读:
    js 中的基本包装类型
    js监听浏览器,关闭,刷新(兼容IE6+,Firefox,Chrome,Safari)
    js 中的 && 与 ||
    程序设计模式的工厂(Factory)模式
    关于MOSS首页不能打开提示“根级别上的数据无效
    获取汉字的拼音的首个字母方法
    XmlDocument扩展类
    初次体验Android,过程很艰辛!
    用Python写个翻译工具
    开发经验是修炼设计模式的基石
  • 原文地址:https://www.cnblogs.com/linding/p/16134991.html
Copyright © 2020-2023  润新知