• Webpack 备忘录


    Webpack 属于在项目中配置一次就很少改动的那种工具,但这样就导致新项目再配置 Webpack 时会有些生疏,所以将 Webpack 核心概念及常用配置记录如下。

    1)核心概念

    Webpack 4.x 之前的核心概念有四个:entry,output,loaders,plugins,4.x 之后增加了 mode。含义如下:

    • Entry:指定 webpack 从哪个 file 开始构建他的依赖关系图,可以有一个和多个,推荐对象表示法;
    • Output:webpack 构建完成后的 bundles 输出位置及 name,其中 name 一般根据 entry 的 file name 动态生成;
    • Loaders:webpack 可以将非 js 文件(css、图像等)通过 loaders 处理成可供我们的应用使用的 modules。也因此在配置文件中,loaders 写在module对象下面,因为 loaders 处理后返回的是有效 modules。
    • Plugins:如果需要额外的功能,比如压缩代码、提取 css、分析打包性能,可使用插件完成;

      Loaders 将特定文件转换为有效 module,而 plugin 扩展性更强

    • Mode:接受developmentproductionnone三个值,一般指定为前两个值的一种,webpack 内部针对不同环境做优化。

    2)配置

    下面介绍的配置都是module.exports的直接属性,比如:

    module.exports = {
    entry: {},
    output: {},
    // loaders 配置位置有些特殊,不知直接写 loaders 对象,而是将它鞋子啊 module 对象下面
    // 因为 loaders 处理完后返回的是有效 modules
    // 不是 复数!!!
    module: {},
    // plugin 是数组
    // 是 复数!!!
    plugins: []
    };

    同时,文件头引入相应依赖的过程省略了,实际开发中请自行引入。

    2.1 Entry

    Entry 指定哪些文件作为 webpack 打包入口,可以有一个或者多个:

    entry: {
    index: './src/js/view/index.js',
    people: './src/js/view/people.js',
    login: './src/js/view/login.js',
    }

    2.2 Output

    Output 指明 Webpack 处理完成后的 bundles 输入位置及文件 name:

    output: {
    // 输入目录,这里使用了 node 的 path 模块
    path: path.resolve(__dirname, 'dist'),
    // [] 里面的内容表示变量
    filename: 'js/[name].[contenthash:8].js',
    // 静态资源在 html 引用的公共目录
    publicPath: '/',
    }

    Output 配置相对复杂:

    2.2.1 output.path

    即输出文件目录,推荐使用绝对路径,其中__dirname在 Node 的 path 模块中表示当前目录(即 webpack.config.js 的目录)的绝对路径,resolve方法可以拼接两个目录;

    后一个目录可以加/也可以不加,此处无差别

    2.2.2 output.filename

    表示输出文件名称,这里注意,[]中是变量,其中name由在 entry 中定义的文件名决定,并且,有多少个输入文件,对应有多少个输出文件,后面的contenthash:8表示 chunk 的 hash 值,后文介绍;

    实际输出文件一般多于输入文件,因为提取公共代码、设置 sourcemap 都会单独生成输出文件

    2.2.3 output.publickPath

    这个配置想最为复杂,它的作用是配合 loaders 或 plugins 中设置的资源路径,指定静态文件(css、js、img)插入 html 中的引用路径,简单讲,就是 对输出的静态资源进行目录管理。这里直接使用 详解 Webpack2 的那些路径的描述,只稍作说明。

    静态资源最终引用路径计算公式:

    html 静态资源路径 = output.publicPath + loaders/plugins 中设置的资源路径

    比如:

    // publicPath 设置
    output.publicPath = '/static/'

    // JS output.filename 配置
    {
    filename: 'js/[name].js'
    }
    // JS 最终访问路径为
    output.publicPath + 'js/[name].js' = '/static/js/[name].js'

    // 图片资源, file-loader,这里省略了 file-loader 其他配置,只列出指定输出路径及文件名的配置
    {
    name: 'img/[name].[ext]'
    }
    // 图片最终的访问路径为
    output.publicPath + 'img/[name].[ext]' = '/static/img/[name].[ext]'

    // CSS,ExtractTextPlugin 为提取 CSS 的插件
    new ExtractTextPlugin("css/style.css")
    // CSS 最终访问路径为
    output.publicPath + 'css/style.css' = '/static/css/style.css'

    我们上述指定publicPath为绝对路径,实际上,也支持相对路径,相对于index.html,用的比较少。

    publicPath默认为空字符串,但为了使输出目录更加条理,推荐使用publicPath对静态资源进行目录管理。

    2.3 Loaders

    Loaders 的配置写在module对象中,如前所述,因为 loaders 最终返回的是有效 modules,故使用了module命名,注意是单数。这节介绍 loaders 配置及常用 loaders 两部分内容。

    2.3.1 loaders 配置

    loaders 规则写在 module.rules 里面(不知道为什么不直接写在 module 中),其中 rules 是个数组,可接受一个或多个 loader 配置。两点需要注意:

    1)如果某类型需要多个 loader 进行处理,在 use 中按 从右往左 的顺序流式处理;
    2)每个 loader 可以进行额外配置。

    module: {
    // 含有 rules,这一个属性
    // rules 是数组,每个元素对应一个 loader 配置
    // loader 配置本身是个对象
    rules: [
    {
    // 正则匹配文件,最终匹配的是一个路径,具体间解析 zepto 源文件时的设置
    test: /.scss$/,
    // 使用的 loader,可以字符串(单个 loader)、数组(多个 loader)
    // 如果是数组,从右往左开始解析
    use: ['style-loader', 'css-loader', 'scss-loader'],
    },
    {
    test: /.(png|jpg|jpeg|svg|gif)$/,
    // 每个 loader 可进行额外配置
    use: [
    {
    loader: 'file-loader',
    options: {
    name: 'img/[name].[ext]',
    },
    },
    ],
    },
    ],
    },

    2.3.2 常用 loader

    • babel-loader:es6+语法转换;
    • html-loader:解析 html 文件;
    • css-loader:解析 css 文件;
    • sass-loader:解析 scss/sass 文件;
    • style-loader:将解析后的 css 嵌入 js;
    • file-loader:解析图片文件;
    • url-loader:具有 file-loader 的全部功能,同时可以提取小图片为 base64(如果开启 HTTP2 这样增大 静态资源体积反而不好?);
    • postcss-loader:完成 css 自动化处理,比如添加前缀、压缩 css、自动生成雪碧图等

      postcss 本身支持插件扩展,常用的有 autoprefixer、cssnano、postcss-sprites,更多参考官网介绍
      要在 css-loader 之前处理 css:use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader']

    Vue 相关

    • vue-loader:解析 vue 文件;
    • vue-style-loader:解析 vue 中的样式文件。

    2.4 Plugins

    同 loaders 一样,分配置和常用 plugins 两部分

    2.4.1 plugins 配置

    Plugins 使用相对简单,配置项写在plugins(复数)数组中,元素为插件的实例(通过new调用),生成实例的时候可接收参数,具体看相应插件文档。见下面代码及注释:

    plugins: [
    // 带有参数
    new CleanWebpackPlugin(['dist']),
    new MiniCssExtractPlugin({
    filename: 'css/[name].[contenthash:8].css',
    chunkFileName: '[id].[contenthash:8].css',
    }),
    // 直接调用
    new UglifyJsPlugin(),
    ],

    2.4.2 常用 plugins

    • html-webpack-plugin:将构建后的静态文件动态插入 html 中;
    • webpack-merge:实际项目中,一般将 webpack 配置文件拆分为 base、dev、pro,这个插件用户合并配置文件;
    • webpack.HotModuleReplacementPlugin:热更新插件,webpack 内置;
    • clean-webpack-plugin:清除指定文件夹,一般是构建的目录(大型项目慎用,更新需要时间);
    • mini-css-extract-plugin:提取 css 文件,减小增量更新成本(需要在module代理 style-loader 处理 css);

      style-loader 先把 css 嵌入 js 使其变成有效 module,这个插件将 css 从 js 中分离出来。这个过程不矛盾,因为最开始 webpack 无法处理 css 文件,所以需要 css-loader, style-loader 处理,嵌入到 js 中的 css mini-css-extract-plugin 可以识别并提出。这个插件是 webpack 4.x 新引入,代替 extract-text-webpack-plugin。

    • optimize-css-assets-webpack-plugin:将提取出的 css 做进一步优化;
    • uglifyjs-webpack-plugin:不仅仅是压缩代码,还进行了 tree shaking 工作
    • webpack-bundle-analyzer:打包后文件图形化展示工具,一目了然各文件体积;
    • ProvidePlugin:webpack 内置,提取第三方库的 api,比如通过 $ 符号调用 jq

      如果不是通过 npm 安装,而是直接在项目中引入,需要配合resolve的别名使用,不然找不到

    2.5 Mode

    接受developmentproductionnone三个值,一般取前两个值之一,指定构建环境,webpack 本身会做相应优化(配合optimization使用,下文会有更多介绍),同时省去在命令行中指定构建环境的过程。

    // 相当于 webpack --mode=production
    mode: 'production',

    2.6 其他

    上述配置对应 Webpack 核心概念,除此之外,还有下面配置项很常用。

    2.6.1 optimization

    这个选项是在 Webpack 4.x 中引入的,最常用的配置是代替CommonsChunkPlugin提取公共代码。下面直介绍提取公共 JS 代码的配置:

    optimization: {
    splitChunks: {
    // 只对入口文件处理
    chunks: 'initial',
    cacheGroups: {
    vendor: {
    // split `src/js/vendor` 目录下共用的代码到`vendor.js`
    // 这里指的是第三方库,很少改动,故单独做持久化缓存
    // 注意,目前 splitChunks 在 split css 时有 bug?现在只 split js 代码
    // 不推荐直接匹配 node_modules 文件夹,如上,因为涉及 css,打包后 css 错误
    test: /src/js/vendor//,
    name: 'vendor',
    // 只要第三方库,只要 > 0B, 就 split 出来
    minSize: 0,
    // 只要被引用一次,也要分离出来
    minChunks: 1,
    // priority: 10,
    // enforce: true,
    },
    common: {
    // split `src/js/common` 目录下共用的代码到`common.js`
    // 这里是自定义的公共 js 代码,改的频率比第三方库要高,但比具体页面的 js 文件低
    // 故单独 split 出来做持久化缓存
    // 不一定共用才 split ,很少改动的也 split 出来,如上的 vender.js
    // 但此处至少两个共用 minChunks 才抽出
    test: /src/js/common//,
    name: 'common',
    minChunks: 2,
    minSize: 0,
    // priority: 9,
    // enforce: true,
    },
    },
    },
    },

    2.6.2 devtool

    devtool最常用功能是配置 sourcemap,生产和开发环境一般使用不同的 sourcemap,配置如下:

    // 生产环境
    devtool: 'source-map',

    // 开发环境
    devtool: 'cheap-module-eval-source-map',

    关于各配置项的含义可参考Devtool和这片文章Webpack 的 devtool 和 source maps

    sourcemap 是构建后的代码 map 到源代码的映射表,便于定位 bug,具体的映射关系存储在构建后的.map文件中。

    NOTE: 这里我们在生产环境使用的是 source-map 选项而不是其他,是因为目前 webpack 在开启 uglifyjs-webpack-plugin 优化代码后,其他模式下的 sourcemap 选项无效,官网对此也有说明。虽然使用source-map选项生成的映射表比较大,但只有开启开发者工具的时候.map文件才会加载,这意味着 映射表文件的大小不影响正常用户的访问体验,但是否压缩 js 文件对正常用户有直接影响,故,现阶段,生产环境 devtool 使用 source-map 选项。

    2.6.3 devServer

    开发环境为了更好的开发体验,可以开启热更新等功能,在devServer中配置:

    devServer: {
    open: true,
    hot: true,
    },

    需要在插件中进行下面配置:

    plugins: [
    new webpack.HotModuleReplacementPlugin(),
    ],

    NOTE:如果传统多页面项目,在入口文件后面添加下面代码,配合热更新:

    // 配合 webpack 配置实现热更新
    if (module.hot) {
    module.hot.accept();
    }

    多页面目前只有在 JS 或者 CSS 文件改变的时候实现了热更新,如果是模版(html)文件改变,没有实现热更新(可以实现自动刷新页面,但感觉很鸡肋,如果改动了模版文件,手动刷新)。

    devServer 还可以设置反向代理,后续填坑。

    2.6.4 resolve

    resolve 选项可以指定如何解析 modules,更多是通过设置 alias 告诉 webpack 去哪找文件解析。比如,如果在项目中通过文件的形式引入的 jq,那在使用 ProvidePlugin 对 jq 进行解析的时候,就需要通过设置别名的形式告诉 webpack 去哪找 jq 源文件。

    resolve: {
    alias: {
    // 后面加 $ 符号表示精确匹配
    jquery$: path.resolve(__dirname, 'path/jquery.min.js');
    }
    }


    // 解析插件
    new webpack.ProvidePlugin({
    // 加载 jquery
    $: 'jquery',
    }),

    3)常见需求详细配置

    下面记录针对具体需求的完整代码描述。

    3.1 引入第三方库

    比如,jq,zepto 等,如果通过 CDN 可以在项目中直接使用 $ 符号,但如果是通过 npm 安装到本地,甚至直接将第三方库源文件写在项目中,那是无法直接使用 $ 符号这种调用方式的,需要使用上文介绍的 ProvidePlugin 插件。当然,还有细节需要注意,见下面详细代码。

    3.1.1 使用 npm 引入

    相对于直接引入,使用 npm 可以省去我们手动指定 module 路径的麻烦,已 zepto 为例:

    使用 zepto 时,直接使用 ProvidePlugin 会报错,具体参考这片文章如何在 webpack 中引入未模块化的库,如 Zepto

    // 处理 zepto 模块化问题,需要安装 exports-loader 和 script-loader
    {
    test: require.resolve('zepto'),
    use: ['exports-loader?window.Zepto', 'script-loader'],
    },

    // 给 zepto 设置别名,可以任何名称
    plugins: [
    new webpack.ProvidePlugin({
    $: 'zepto',
    }),
    ],

    3.1.2 直接在项目中引入源文件

    同 npm 不同之处在于我们需要手动指定 zepto 的路径,如下

    // 设置别名
    resolve: {
    alias: {
    zepto$: path.resolve(__dirname, 'src/js/vendor/zepto.min.js'),
    },
    },

    // 处理 zepto 模块化问题
    {
    // test 路径也要跟着改变,不知道为什么不能用上面指定的别名。。
    test: path.resolve(__dirname, 'src/js/vendor/zepto.min.js'),
    use: ['exports-loader?window.Zepto', 'script-loader'],
    },

    // 给 zepto 设置别名,可以任何名称
    plugins: [
    new webpack.ProvidePlugin({
    $: 'zepto',
    }),
    ],

    3.2 提取 css

    为了能将 css 从 js 中提取出来,需要在 module 中将 style-loader 替换为 MiniCssExtractPlugin.loader,见下面代码:

    module: {
    rules: [
    {
    test: /.css$/,
    use: [MiniCssExtractPlugin.loader, 'css-loader'],
    },
    {
    test: /.scss$/,
    use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
    },
    ],
    },

    plugins: [
    new MiniCssExtractPlugin({
    filename: 'css/[name].[contenthash:8].css',
    // 这个干嘛用?
    chunkFileName: '[id].[contenthash:8].css',
    }),
    ],

    3.3 缓存控制

    主要一下几个方面:

    • 提取 css,添加 contenthash(使用 mini-css-extract-plugin);
    • 提取公共代码(工具函数等,更改频率相比业务代码要小),添加 contenthash,使用 webpack 自带 splitChunks 提取;
    • 提取第三方库代码(比如 jquery,更改频率相比公共代码还要小),添加 contenthash,使用 webpack 自带 splitChunks 提取;
    • 图片可以直接打 hash(图片文件添加 hash 并不一样,也不会随每次构建改变,还不知原理,反正可以工作,待填坑)。
  • 相关阅读:
    Java学习第十五章 之 Map、可变参数、Collections
    Java第十四章 之 List、Set
    Java学习第十三章 之 常用API
    通过shell终端上传下载文件
    javamail邮件发送
    linux防火墙添加例外端口shell脚本
    MySQL批量更新
    MySQL返回列名
    发现一个有意思的东西
    struts2,action方法自动执行两次
  • 原文地址:https://www.cnblogs.com/sunshq/p/9414218.html
Copyright © 2020-2023  润新知