webpack从配置到跑路v2

2020-05-23  本文已影响0人  hellomyshadow

入口

打包默认的入口文件:src/index.js
配置入口的属性:entry

//webpack.config.js
module.exports = {
    entry: './src/index.js'
}

entry的属性值可以是一个字符串、数组或对象

出口

默认出口文件:dist/main.js,配置编译文件的输出属性为 output

const path = require('path');
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),    // 必须是绝对路径
        filename: 'bundle.js',  // 输出一个名为 `bundle.js` 的文件
        publicPath: '/'    //通常是CDN地址
    }
}

清理dist目录

每次编译并不会删除旧文件,如果文件名中带有hash,那每次打包都会生成一个新的文件;插件clean-webpack-plugin 可以在每次打包前清理输出目录中的文件。

   npm i -D clean-webpack-plugin

   // webpack.config.js
   const { CleanWebpackPlugin } = require('clean-webpack-plugin');
   plugins: [
       new CleanWebpackPlugin(),
   ]

CleanWebpackPlugin可以接受一个配置对象,如果不传,则去找output.path,默认是dist目录;
配置对象可以做很多事,比如不希望dll目录每次都清理:

   new CleanWebpackPlugin({
        // 不清理 dll 目录下的文件
        cleanOnceBeforeBuildPatterns: ['**/*', '!dll', '!dll/**']
   })

更多配置项

静态文件的拷贝

有时候,我们需要使用已有的JS文件、CSS文件、图片等静态资源,但不需要webpack编译,比如在index.html中引入public目录下的js/css/image,编译后肯定找不到了,而手动拷贝public目录到输出目录必然又容易忘记。
插件copy-webpack-plugin 支持将单个文件或整个目录拷贝到构建(输出)目录。

npm i copy-webpack-plugin -D

// webpack.config.js
const CopyWebpackPlugin = require('copy-webpack-plugin');
plugins: [
    new CopyWebpackPlugin([
        {
            // 拷贝 public 目录下的所有js文件 到 dist/js 目录中
            from: 'public/js/*.js',
            to: path.resolve(__dirname, 'dist', 'js'),
            flatten: true
        },
        {
            // 把 /src/assets 拷贝到 /dist/assets
            from: path.join(__dirname, 'src/assets'),
            to: path.join(__dirname, 'dist/assets')
        },
    ])
]

参数 flatten 设置为 true 时,只会拷贝文件,而不会把文件夹路径都拷贝上。
CopyWebpackPlugin还可以配置忽略哪些文件:ignore

new CopyWebpackPlugin([
    {
        from: 'public/js/*.js',
        to: path.resolve(__dirname, 'dist', 'js'),
        flatten: true
    }
], {
    ignore: ['other.js']   // 忽略 `other.js` 文件
})

ProvidePlugin

ProvidePluginwebpack的内置插件,用于设置全局变量,在项目中不需要import/require就能使用。

new webpack.ProvidePlugin({
  identifier1: 'module1',
  identifier2: ['module2', 'property2']
});

默认查找路径是当前目录 ./**node_modules,当然也可以直接指定全路径。
React、jquery、lodash这样的库,可能在多个文件中使用,把它们配置为全局变量,就不需要在每个文件中手动导入了。

const webpack = require('webpack');
module.exports = {
    //...
    plugins: [
        new webpack.ProvidePlugin({
            React: 'react',
            Component: ['react', 'Component'],
            Vue: ['vue/dist/vue.esm.js', 'default'],
            $: 'jquery',   // npm i jquery -S
            _map: ['lodash', 'map']  // 把lodash中的map()配置为全局方法
        })
    ]
}

配置之后,在项目中可以随便使用$、_map了,且在编写React组件时,也不需要import React/Component了。
ReactVue的到处方式不同,所以配置方式有所区别。vue.esm.js中是通过export default导出的,而React使用的是moudle.exports导出的。
如果项目中启用了eslint,还需要修改其配置问家:

{
    "globals": {
        "React": true,
        "Vue": true,
        //....
    }
}

如果把第三方库手动下载添加到项目目录下,比如/static/js/jquery.min.js
那么可以先给它配置别名,然后再配置为全局变量:

module.exports = {
    //...
    resolve: {
        alias: {  // 配置JS库的路径的别名
            jQuery: path.resolve(__dirname, 'static/js/jquery.min.js')
        }
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: 'jQuery',   // 优先从 alias 别名对应的路径去查找
        })
    ]
}

提取抽离CSS

很多时候我们都会把抽离CSS,单独打包成一个或多个文件,这样既可以提高加载速度,又有利于缓存。
插件mini-css-extract-plugin(新版)extract-text-webpack-plugin(旧版)相比:

mini-css-extract-plugin的安装与配置

    npm i mini-css-extract-plugin -D

    // webpack.config.js
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    module.exports = {
        module: {
            rules: [
                {
                    test: /\.(le|c)ss$/,
                    use: [MiniCssExtractPlugin.loader, //替换之前的 style-loader
                        'css-loader', {
                            loader: 'postcss-loader',
                            options: {
                                plugins: function () {   // [require('autoprefixer')]
                                    return [
                                        require('autoprefixer')()
                                    ]
                                }
                            }
                        }, 'less-loader']
                }
            ]
        },
        plugins: [
            new MiniCssExtractPlugin({
                filename: 'css/[name].css'  // 将css文件放在单独目录下
                // publicPath:'../',
                // chunkFilename: 'css/[id].[hash].css'
            })
        ]
    }

注意:如果output.publicPath配置的是 ./ 这种相对路径,那么如果将css文件放在单独目录下,记得配置MiniCssExtractPluginpublicPath

开发模式下,只有第一次修改CSS才会刷新页面,需要为MiniCssExtractPlugin.loader配置参数:

const ENV = process.env.NODE_ENV

use: [
    {
        loader: MiniCssExtractPlugin.loader,
        options: {
            hmr: ENV === 'development',
            reloadAll: true,
        }
    }, 
    // ...
]

更多配置项 查看mini-css-extract-plugin

CSS压缩

抽离CSS默认不会被压缩,插件optimize-css-assets-webpack-plugin用于压缩CSS

    npm i optimize-css-assets-webpack-plugin -D

    // webpack.config.js
    const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
    plugins: [
        new OptimizeCssAssetsPlugin()
    ],

OptimizeCssPlugin还可以传入一个配置对象:

new OptimizeCssAssetsPlugin({
    assetNameRegExg: /\.css$/g,
    cssProcessor: require('cssnano'),
    cssProcessorPluginOptions: {
        preset: ['default', {discardComments: {removeAll: true}}]
    },
    canPrint: true
})

按需加载JS

很多时候并不需要一次性加载所有js文件,而应该在不同阶段去加载所需要的代码。
webpack内置了强大的代码分割功能,可以实现按需加载--> import()
比如,在点击了某个按钮之后,才需要使用对应js文件中的代码

dom.onclick = function() {
    import('./handle').then(fn => fn.default());
}

import()语法需要 @babel/plugin-syntax-dynamic-import 插件的支持,但预设@babel/preset-env中已经包含了该插件,所以无需单独安装和配置。
npm run build构建时发现,webpack会生成一个新的chunk文件。

webpack遇到 import(xxxx) 时:

热更新

当前修改js文件会刷新整个页面,热更新会在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面。
热更新:即模块热替换,HMR,主要通过以下几种方式来显著加快开发速度

首先配置 devServer.hottrue,然后使用webpack的内置插件HotModuleReplacementPlugin

    modules.exports = {
        devServer: {
            hot: true,  // 开启热更新
            // hotOnly: true,
            contentBase: './dist',
            // ...
        },
        plugins: [
            new webpack.NamedModulesPlugin(),
            new webpack.HotModuleReplacementPlugin()
        ]
    }

虽然修改CSS会自动热更新,但修改JS代码仍然是整个页面刷新,还需要在JS文件中告诉webpack接收更新的模块

    // 修改入口文件 src/index.js
    if(module && module.hot) {
        module.hot.accept()
    }

多页应用打包

我们的应用不一定是一个单页应用,而是一个多页应用,那么webpack如何进行打包呢。
插件html-webpack-plugin可以配置多个,每一个HtmlWebpackPlugin对应一个页面,其filename属性控制打包后的页面名称,默认值是index.html,所以对于多页应用,一定不能省略filename

// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    entry: {
        index: './src/index.js',
        login: './src/login.js'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].[hash:6].js'
    },
    //...
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html',
            filename: 'index.html' // 打包后的文件名
        }),
        new HtmlWebpackPlugin({
            template: './public/login.html',
            filename: 'login.html'
        }),
    ]
}

查看打包后的index.htmllogin.html发现,entry中配置的入口文件会在每个页面中都引入一次。而我们想要的是index.html只引入./src/index.jslogin.html只引入./src/login.js
HtmlWebpackPlugin提供了一个chunks属性,接受一个数组,用于指定将哪些入口js文件引入到页面中。

plugins: [
    new HtmlWebpackPlugin({
        template: './public/index.html',
        filename: 'index.html',
        chunks: ['index']  // 数组元素为 entry中的键名
    }),
    new HtmlWebpackPlugin({
        template: './public/login.html',
        filename: 'login.html',
        chunks: ['login']
    }),
]

还有一个和chunks相对立的属性excludeChunks,指定哪些文件不想引入到页面中。

resolve

resolve 配置 webpack 如何查找模块所对应的文件。
webpack内置JavaScript模块化语法解析功能,默认会采用模块化标准里约定好的规则去查找,但也可以根据自己的需要修改默认规则。

上一篇下一篇

猜你喜欢

热点阅读