webpack 实践记录

webpack 实践记录(v4.26.0)

  1. 初始化 npm init
  2. 安装webpack npm install --save-dev webpack webpack-cli
  3. 安装本地开发环境 webpack-dev-server npm install --save-dev webpack-dev-server
  4. 建立目录结构
zero
|-package.json
|-/js
  |-/common
    |-common.js
  |-login.js
|-/imgs
  |-mobile.png
  |-captcha.png
  |-username.png
  |-317x70.png
|-/less
  |-common.less
  |-login.less
|-/pages
  |-login.html
|-node_modules
|-webpack.config.js
复制代码
  1. 配置 webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    mode:'development',
    entry:{
        login:'./js/login.js'
    },
    output:{
        path:path.resolve(__dirname,'dist'),
        filename:'js/[name].bundle.js'
    },
    devServer:{
      contentBase:path.resolve(__dirname,'dist'),
      open:true,
      openPage:'pages',
      index:'login.html'
    },
    module:{
        rules:[{
            test:/\.js$/,
            exclude:/node_modules/,
            use:[{
                loader:'babel-loader',
                options:{
                    presets:['@babel/preset-env'],
                    plugins:['@babel/plugin-syntax-dynamic-import',['@babel/plugin-transform-runtime',{
                        corejs:2
                    }]]
                }
            }]
        },{
            test:/\.html/,
            use:[{
                loader:'html-loader',
                options:{
                    attrs:[':src']
                }
            }]
        },{
            test:/\.less$/,
            use:['style-loader','css-loader','less-loader']
        },{
            test:/\.(png|jpg|svg|gif)$/,
            use:[{
                loader:'url-loader',
                options:{
                    limit:1024,
                    name:'[name].[ext]',
                    publicPath: '',
                    outputPath:'imgs/'
                }
            }]
        }]
    },
    plugins:[
        new HtmlWebpackPlugin({
            filename:'pages/login.html',
            template:'./pages/login.html'
        })
    ]
}
复制代码
  1. login.js
import '../less/common.less'
import '../less/login.less'
import './common/common.js'
复制代码
  1. 修改 package.json 文件
{
    ...
    scripts:{
        "dev":"set NODE_ENV=development&& webpack-dev-server --config webpack.config.js",
        "build":"set NODE_ENV=production&& webpack --progress --config webpack.config.js"
    }
    ...
}
复制代码

开发:npm run dev 打包:修改 mode:'production' 然后执行 npm run build

dist
|-/imgs
  |-*.png
|-/js
  |-login.bundle.js
|-/pages
  |-login.html
复制代码

打开 login.html 页面正常。查看 login.bundle.js 发现所有的 cssjs 都被打包到了这一个文件中。这并不是我们想要的结果。并且针对开发环境和生产环境没有进行区分。我们同时拆分 webpack.config.js 使其更加灵活。

  1. 公共配置 webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
    entry:{
        login:'./js/login.js'
    },
    output:{
        path:path.resolve(__dirname,'dist'),
        filename:'js/[name].bundle.js'
    },
    module:{
        rules:[{
            test:/\.js$/,
            exclude:/node_modules/,
            use:[{
                loader:'babel-loader',
                options:{
                    presets:['@babel/preset-env'],
                    plugins:['@babel/plugin-syntax-dynamic-import',['@babel/plugin-transform-runtime',{
                        corejs:2
                    }]]
                }
            }]
        },{
            test:/\.html/,
            use:[{
                loader:'html-loader',
                options:{
                    attrs:[':src']
                }
            }]
        }]
    },
    plugins:[
        new CleanWebpackPlugin(['dist']),
        new HtmlWebpackPlugin({
            filename:'pages/login.html',
            template:'./pages/login.html'
        })
    ]
}
复制代码
  1. 开发配置 webpack.dev.js
const path = require('path')
const merge = require('webpack-merge')
const common = require('./webpack.common.js')
module.exports = merge(common,{
    mode:'development',
    devServer:{
      contentBase:path.resolve(__dirname,'dist'),
      open:true,
      openPage:'pages',
      index:'login.html',
    },
    devtool:'inline-source-map',
    module:{
        rules:[{
            test:/\.less$/,
            use:['style-loader','css-loader','less-loader']
        },{
            test:/\.(png|jpg|svg|gif)$/,
            use:[{
                loader:'file-loader',
                options:{
                    name:'[name].[ext]',
                    publicPath:'../imgs/',
                    outputPath:'imgs/'
                }
            }]
        }]
    }
})
复制代码
  1. 生产配置 webpack.prod.js
const path = require('path')
const merge = require('webpack-merge')
const common = require('./webpack.common.js')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = merge(common,{
    mode:'production',
    output:{
        publicPath:'https://xyz.com/js/'
    },
    devtool:'source-map',
    module:{
        rules:[{
            test:/\.less$/,
            use:[MiniCssExtractPlugin.loader,'css-loader','less-loader']
        },{
            test:/\.(png|jpg|svg|gif)$/,
            use:[{
                loader:'url-loader',
                options:{
                    limit:1024,
                    name:'[name].[ext]',
                    publicPath: 'https://xyz.com/img/',
                    outputPath:'imgs/'
                }
            }]
        }]
    },
    plugins:[
        new MiniCssExtractPlugin({
            filename:'css/[name].css'
        }),
        new OptimizeCssAssetsPlugin({})
    ]
})
复制代码

同时修改 package.json

{
    ...
    scripts:{
        "dev":"set NODE_ENV=development&& webpack-dev-server --config webpack.dev.js",
        "build":"set NODE_ENV=production&& webpack --progress --config webpack.prod.js"
    }
    ...
}
复制代码

打包:

dist
|-/css
  |-login.css
|-/img
  |-*.png
|-/js
  |-login.bundle.js
  |-login.bundle.js.map
|-/pages
  |-login.html
复制代码

至此基本满足需求。 HtmlWebpackPlugin 插件会把打包出来的资源文件自动插入到 html 模板当中。 但是我们往往还需要引入第三方文件 例如 jquery。 安装 npm install --save jquery@1.11.3 公共类型的第三方文件使用方式:

webpack.common.js
{
    plugins:[
        ...
        new webpack.ProvidePlugin({
            $:'jquery',
            jQuery:'jquery'
        })
        ...
    ]
}
复制代码

这样我们就可以直接使用 $login.js

import './common/common.js'
import '../less/common.less'
import '../less/login.less'
$(document).ready(function(){ ... })
复制代码

其他非公共第三方文件安装后通过 import 引用。

代码拆分

第三方的公共文件我们不会轻易更换,所以一般要把第三方文件单独打包,这样便于缓存。使用 optimization.splitChunks 进行代码拆分

webpack.common.js
{
    ...
    optimization:{
        splitChunks:{
            chunks:'all'
        },
        runtimeChunk:{
            name:'runtime'
        }
    }
}
复制代码

打包:

dist
|-/css
  |-login.css
|-/img
  |-*.png
|-/js
  |-login.bundle.js
  |-runtime.bundle.js
  |-vendors~login.bundle.js
  |-*.js.map
|-/pages
  |-login.html
复制代码

默认的代码拆分规则

  • 新的被共享的代码块或者是 node_modules 中的模块
  • 新的被共享的代码块在 min+gz 之前大于 30kb
  • 按需加载时,并行请求数量 <=5
  • 页面初始化时,并行请求数量 <=3
{
    ...
    optimization:{
        splitChunks:{
        chunks: 'async',
            minSize: 30000,
            maxSize: 0,
            minChunks: 1,
            maxAsyncRequests: 5,
            maxInitialRequests: 3,
            automaticNameDelimiter: '~',
            name: true,
            cacheGroups: {
                vendors: {
                  test: /[\\/]node_modules[\\/]/,
                  priority: -10
                },
                default: {
                  minChunks: 2,
                  priority: -20,
                  reuseExistingChunk: true
                }
            }
        }
    }
}
复制代码

往往我们需要自定义代码拆分的规则。自定义配置

{
    output:{
        filename:'js/[name].bundle.js',
        chunkFilename:'js/[name].bundle.js'
    },
    plugins:[
        ...
        new MiniCssExtractPlugin({
            filename:'css/[name].css',
            chunkFilename:'css/[name].css'
        })
    ],
    ...
        optimization:{
            splitChunks:{
                chunks:'all',
                cacheGroups:{
                    vendors:{
                        test:/node_modules/,
                        name:'vendors',
                        priority:10
                    },
                    common:{
                        test:/(common\.js|common\.less)/,
                        name:'common',
                        priority:1,
                        enforce:true
                    }
                }
            },
            runtimeChunk:{
                name:'runtime'
            }
    }
}
复制代码

打包:

dist
|-/css
  |-login.css
  |-common.css
|-/img
  |-*.png
|-/js
  |-common.bundle.js
  |-login.bundle.js
  |-runtime.bundle.js
  |-vendors~login.bundle.js
  |-*.bundle.js.map
|-/pages
  |-login.html
复制代码

缓存

同时我们还希望当我们对文件进行了修改后可以让浏览器请求最新的文件。我们需要对输出文件的文件名进行控制

{
    ...
    output:{
        filename:'js/[name].[contenthash].js'
    },
    ...
    plugins:[{
        new MiniCssExtractPlugin({
            filename:'css/[name].[contenthash].css'
        })
    }],
    optimization:{
        namedChunks:true,
        moduleIds:'hashed'
    }
}
复制代码

这样当有文件发生变化时,打包后就只有对应修改的文件和 runtime.js 文件会发生变化。

多页面

当有多个页面时,我们需要多个入口文件

zero
|-package.json
|-/js
  |-/common
    |-common.js
  |-login.js
  |-company.js
|-/imgs
  |-mobile.png
  |-captcha.png
  |-username.png
  |-317x70.png
|-/less
  |-common.less
  |-login.less
  |-company.less
|-/pages
  |-login.html
  |-company.html
|-node_modules
|-webpack.common.js
|-webpack.dev.js
|-webpack.prod.js
复制代码

webpack.common.js

{
    entry:{
        login:'./js/login.js',
        company:'./js/company.js'
    },
    ...
    plugins:[{
        new HtmlWebpackPlugin({
            filename:'pages/login.html',
            template:'./pages/login.html',
            chunks:['runtime','vendors','common','login']
        })
        new HtmlWebpackPlugin({
            filename:'pages/company.html',
            template:'./pages/company.html',
            chunks:['runtime','vendors','common','company']
        }),
    }]
}
复制代码

虽然这样可以达到目的,但是每当有新文件增加时就需要修改配置文件,可以通过读取文件目录自动生成入口配置和模板配置

const glob = require('glob')
let entry = {};
glob.sync('./js/*.js').forEach((file)=>{
    let filename = path.basename(file,'.js');
    entry[filename] = file;
});
let pages = [];
glob.sync('./pages/*.html').forEach((file)=>{
    let filename = path.basename(file,'.html');
    pages.push(new HtmlWebpackPlugin({
        filename:`${filename}.html`,
        template:file,
        chunks:['runtime','vendors','common',filename]
    }));
});
webpack.common.js
{
    entry:entry,
    ...
    plugins:[
        ...pages
    ]
}
复制代码

使用到的loader和plugin说明

loader的作用顺序是从后往前

  • babel-loader ES6转ES5

  • style-loader css-loader 解析 css 并插入到模板 style 标签中

  • less-loader 解析less

  • file-loader url-loader 解析资源文件 图片、字体等

  • html-loader 解析 html 文件中的图片

  • html-webpack-plugin 生成模板文件把资源自动插入到模板中

  • clean-webpack-plugin 打包是清理上一次的打包文件

  • mini-css-extract-plugin css分离插件

  • optimize-css-assets-webpack-plugin css压缩插件

  • webpack.ProvidePlugin 自动加载模块

  • webpack.DefinePlugin 创建全局常量

  • SplitChunksPlugin 代码分割

  • webpack-bundle-analyzer 资源分析

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值