一路繁花似锦绣前程
失败的越多,成功才越有价值

导航

 

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")

const processDefinePlugin = {}
Object.keys(process.env).forEach(key => {
  processDefinePlugin[`process.env.${key}`] = JSON.stringify(process.env[key])
})

module.exports = {
    plugins: [
        new HtmlWebpackPlugin({
            template: "./public/index.html",
            title: "孟美岐" // htmlWebpackPlugin.options.title
        }),
        new DefinePlugin({
            ...processDefinePlugin,
            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"
  },
}
posted on 2022-04-12 14:23  一路繁花似锦绣前程  阅读(78)  评论(0编辑  收藏  举报