教你用webpack4打造优化到极致的活动页面

2,540 阅读5分钟

前言

一般待过大公司的同学都知道,一个成熟稳定的公司肯定需要销售产品或推广产品来盈利,推广自己的产品则需要活动页面来引流。下面,我将分享自己在公司的探索经历,教你用webpack4打造优化到极致的活动页面。

项目结构

spcial为项目名称,build为webpack配置,common为公用文件,dist为打包后的文件(这里就是要上线的活动页面),src就是对应日期所做的活动页面

项目开始

安装webpack webpack-cli,配置webpack.config.js.

npm init
npm i webpack webpack-cli -S

+ webpack-cli@3.3.4
+ webpack@4.35.0

webpack.config.js:

const path = require('path');
module.exports = {
    entry: {
        main: 'src/20190601/index.js'
    },
    module: {
        rules: [
            {

            }
        ]
    },
    output: {
        path: path.resolve(__dirname,'./dist/20190601'),
        filename: 'index.js',
        chunkFilename: '[name].js' // 代码拆分后的文件名
    }
}

配置loader选项

  • css样式处理:安装postcss-loader,less, less-loader,css-loader,style-loader并添加配置
rules: [
    {
        test: /\.css$/,
        use: [
            'style-loader',
            'css-loader',
            'postcss-loader'
            
        ]
    },
    {
        test: /\.less$/,
        use: [
            'style-loader',
            {
                loader: 'css-loader',
                options: {
                    importLoaders:2,    //防止样式嵌套css失效
                }
            },
            'less-loader',
            'postcss-loader'
        ]
    },
]

当然,要使用postcss-loader需要额外的配置安装autoprefixer:npm i autoprefixer -D, 在special根目录增加文件postcss.config.js

module.exports = {
  plugins: [
  	require('autoprefixer')
  ]
}
  • 图片文件处理:安装html-loader,url-loader,file-loader并添加配置
{
    test: /\.(eot|svg|ttf|woff|woff2)$/,
    use: ['file-loader']
},
{
    test: /\.(html)$/,
    use: {
        loader: 'html-loader',
        options: {
            attrs: ['img:src', 'img:data-src', 'audio:src','title'],
            //minimize: true
        }
    }
},
{
    test: /\.(jpg|png|gif|webp)$/,
    use: [{
        loader: 'url-loader',
        options: {
            name: '[name].[ext]',
            outputPath: 'images/'
        }
    }]
}
  • es6+处理:安装@babel/core, @babel/core, @babel/polyfill,@babel/preset-env, babel-loader并添加配置
{
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader'
}

新建一个.babelrc到根目录

{
    "presets": [
    	[
            "@babel/preset-env",
            {
                "useBuiltIns": "usage"
            }
    	]
    ]
}

Plugins 的安装

我们先安装以下插件

  • HtmlWebpackPlugin():根据html模板,将css,js打包生成对应的html中
  • CleanWebpackPlugin():清除上次打包的文件
npm i clean-webpack-plugin html-webpack-plugin -D

webpack.config.js新增配置
import HtmlWebpackPlugin from 'html-webpack-plugin'
import {CleanWebpackPlugin} from 'clean-webpack-plugin' //clean-webpack-plugin版本>=3.0.0写法

plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
        template:  'src/20190601/index.html'
    })
],

运行项目测试

我自己新建一个活动,然后测试

npm run webpack --mode production

图中可见生成dist目录,dist目录生成对应的文件

【重点】项目优化

分开配置webpack.config.js文件,便于我们后期操作,安装webpack-merge

  • webpack.base.js 生产环境和开发环境公用代码,配置如下:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');

const Config = {
    entry: {
        main: ''
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },
            {
                test: /\.(eot|svg|ttf|woff|woff2)$/,
                use: ['file-loader']
            },
            {
                test: /\.(html)$/,
                use: {
                    loader: 'html-loader',
                    options: {
                        attrs: ['img:src', 'img:data-src', 'audio:src','title'],
                        //minimize: true
                    }
                }
            },
            {
                test: /\.(jpg|png|gif|webp)$/,
                use: [{
                    loader: 'url-loader',
                    options: {
                        name: '[name].[ext]',
                        outputPath: 'images/'
                    }
                }]
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin()
    ],
    output: {
        
    }
    
}
function setDate(date) {
    Config.entry.main = `./src/${date}/index.js`;
    Config.plugins.push(new HtmlWebpackPlugin({
        template:  `src/${date}/index.html`,
       // minify: true
    }));
    Config.output = {
        path: path.resolve(__dirname,'../dist/'+date),
        filename: 'index.js',
        chunkFilename: '[name].js' // 代码拆分后的文件名
    }
}
setDate('20190614')

module.exports = Config;

这里把某些配置提出来是因为每个活动页面名称不同,打包的时候改变setDate()即可

  • webpack.dev.js 开发环境,配置如下:
const merge = require('webpack-merge');
const Config = require('./webpack.base.js');

const devConfig = {
    mode: 'development',
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'postcss-loader'
                ]
            },
            {
                test: /\.less$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders:2,
                        }
                    },
                    'less-loader',
                    'postcss-loader'
                ]
            },
        ]
    }
}

module.exports = merge(Config,devConfig)
  • webpack.prod.js 生产环境,配置如下:
const merge = require('webpack-merge');
const Config = require('./webpack.base.js')

const prodConfig = {
    mode: 'production',
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader'
                    'css-loader',
                    'postcss-loader'
                ]
            },
            {
                test: /\.less$/,
                use: [
                    'style-loader'
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders:2,
                        }
                    },
                    'less-loader',
                    'postcss-loader'
                ]
            },
        ]
    }
}
Config.output.publicPath = '20190614/';   //挂载到服务器路径

module.exports = merge(Config,prodConfig)
  • 最后改下package.json配置
"scripts": {
    "build": "webpack --config ./build/webpack.prod.js",
    "dev": "webpack-dev-server --config ./build/webpack.dev.js"
},

开发环境优化

安装webpack-dev-server,并在webpack.dev.js添加配置

npm i webpack-dev-server -D
devServer: {
    contentBase: './dist',
    port: 8088,
    proxy: {
        //api代理配置项
    }
},

生产环境优化

  • 安装以下插件并使用: mini-css-extract-plugin(分离样式),optimize-css-assets-webpack-plugin(压缩css), uglifyjs-webpack-plugin(压缩js)虽然生产环境会自动压缩js,但使用插件有更多的选择项
npm i mini-css-extract-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin -D

webpack.prod.js配置如下:

const merge = require('webpack-merge');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const UglifyJsPlugin  = require('uglifyjs-webpack-plugin');
const Config = require('./webpack.base.js')

const prodConfig = {
    mode: 'production',
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'postcss-loader'
                ]
            },
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders:2,
                        }
                    },
                    'less-loader',
                    'postcss-loader'
                ]
            },
        ]
    },
    optimization: {
        minimizer: [
            new OptimizeCSSAssetsPlugin({}),
            new UglifyJsPlugin()
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
			filename: '[name].css',
			chunkFilename: '[name].chunk.css'
        })
    ]
}
Config.output.publicPath = '20190614/';   //挂载到服务器路径

module.exports = merge(Config,prodConfig)

代码分割

代码分割的作用将第三方的插件分割成单独文件,减少index.js体积,减少白屏时间,比如我页面中用到swiper, 那我可以把它分割成一个单独js,webpack.prod.js增加配置如下

optimization: {
    minimizer: [
        new OptimizeCSSAssetsPlugin({}),
        new UglifyJsPlugin()
    ],
    splitChunks: {
        chunks: 'initial',  //initial代表只分割同步代码,async分割异步代码,all分割全部第三方代码
        minSize: 30000, //大约30kb才能分割
        minChunks: 1,
        name: true,
        cacheGroups: {
            swiper: {
                name: 'swiper',
                test: /[\\/]node_modules[\\/]swiper[\\/]/,
                priority: 10
            },
            vendors: {
                name: 'orthers',
                test: /[\\/]node_modules[\\/]/,
                priority: -10
            },
        }
    }
},

异步加载

js异步加载对于单屏的活动页面非常重要,大量减少首批渲染时间,以swiper为例: 安装babel-plugin-transform-dynamic-import-default,因为js异步引入语法import()属于实验性语法

npm i babel-plugin-transform-dynamic-import-default -D

在.babelrc配置

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "useBuiltIns": "usage"
            }
        ]
    ],
    "plugins": ["babel-plugin-transform-dynamic-import-default"]
}

实战引用:

var flag = true;
document.addEventListener('scroll',() => {
    if(flag) {
        import(/* webpackPrefetch: true */ 'swiper').then( ()=> {
            console.log("引入成功")
            var swiper1 = new Swiper('.swiper-container',{
                loop: true,
                pagination:'.swiper-pagination',
                autoplay: 3000,
                speed:1000
            });
        })
        flag = false;
    }
})

tree-shaking

我们知道打包存在es6语法文件时会很大,是因为webpack会默认把所有新js语法打包进去,而@babel/polyfill,会把只用到的新js语法打包进去,具体配置在.babelrc改为"useBuiltIns": "usage"

图片压缩和使用base64编码

安装image-webpack-loader

{
    test: /\.(jpg|png|gif|webp)$/,
    use: [{
        loader: 'url-loader',
        options: {
            name: '[name].[ext]',
            outputPath: 'images/',
            limit: 2048 //小于2k的图片变成base64编码,减少http请求
        }
    },{
        loader: 'image-webpack-loader', //图片过大时修改此配置进行压缩
        options: {
          mozjpeg: {
            progressive: true,
            quality: 100
          },
          pngquant: {
            quality: 90,
            speed: 4
          },
          gifsicle: {
            interlaced: false,
          }
        }
    }]
}

总结

当时想着用webpack来管理活动页面主要是因为提高开发效率和提高产品质量,经测试,同一个活动页面,一个用webpack打包,一个用普通html写,首屏渲染时间至少减少2倍,网速慢情况下性能更突出。 项目已放上github,欢迎同学们去star学习:去github看看