webpack学习

webpack的基本配置

入口文件(entry)

要打包哪个文件

出口文件(output)

要打包到哪里去

加载器(loader)

作用:加载除js文件其他文件的功能(css less 图片)

webpack只能处理js文件,非js文件(css less 图片 字体等处理不了,只能借助加载器处理

一般兼容性的处理都是使用loader

  1. 处理样式 style-loader css-loader less-loader

  2. 处理图片 url-loader file-loader

    区别:

    url-loader 会把图片编译成base64格式 ,打包到bundle中

    ​ 注意: base64 的好处 减少一个http请求,然而,与之同时付出的代价就是css文件体积变大,css文件体检直接影响渲染,导致用户会长时间注视空白屏幕

    file-loader 不会把图片打包到bundle.js中,而是单独生成一个图片

    劣势:单独生成一个图片就要多发送一个图片的http请求

    解决方案:如果图片小,就使用url-loader 编译成base64格式,如果图片大单独生成一个图片文件

    url-loader 是file-loader的一个升级

  3. 处理html 中引入的图片 html -loader

    会有问题 :因为url-loader无法解析html中引入的图片,所以需要html-loader。但是这个也会有问题,因为url-loader默认是使用es6模块化语法,而html引入图片是使用commonjs,所以两种解析方式不一样,就会导致问题。此时需要关闭url-loader中的es6模块化解析,使用commonjs解析。esMoudule:false

  4. 处理字体图标文件 file-loader

插件(plugin)

作用:处理加载器完成不了对的功能,使用插件

一般压缩的功能都是使用插件

ex:

  1. html-webpack-plugin

作用:能够根据指定的模板文件(index.html),自动生成一个新的index.html文件,并且注入到dist文件夹下,能够自动引入js文件

  1. 提取css到css文件中 min-css-extract-plugin

模式(mode)

development:开发模式

production:生产模式

devServer

devServer:{
    //项目构建后的路径
    contentBase:resolve(__dirname,'build'),
    //启动gzip压缩
     compress:true,
    //端口号
    port:3000,
    //自动打开浏览器
    open:true
}

开发环境的基本配置

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.export = {
    entry: './src/index.js',
    output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
    },
    module: {
        rules: [
            //loader的配置
            {
                //处理less资源
                test: /\.less$/,
                use: ['style-loader', 'css-loader', 'less-loader']
            },
            {
                //处理css资源
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                //处理图片资源
                test: /\.(jpg|png|gif)$/,
                loader: 'url-loader',
                options: {
                    limit: 8 * 1024,
                    name: '[hash:10].[ext]'
                },
                //关闭es6模块化
                esMoudle: false
            },
            {
                //处理html中引入图片资源
                test: /\.html$/,
                loader: 'html-loader',
            },
            {
                //处理其他资源
                test: /\.(html|js|css|less|jpg|png|gif)$/,
                loader: 'file-loader',
                options: {
                    name: '[hash:10].[ext]'
                }
            },
        ]
    },
    plugins: [
        //plugins的配置
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    devServer: {
        //项目构建后的路径
        contentBase: resolve(__dirname, 'build'),
        //启动gzip压缩
        compress: true,
        //端口号
        port: 3000,
        //自动打开浏览器
        open: true
    }
}

生产环境的基本配置

css的处理

  • 提取css成单独的文件:mini-css-extract-plugin
  • css兼容性的处理: postcss-loader postcss-preset-env
  • 压缩css:optimize-css-assets-webpack-plugin
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
module.export = {
    entry: './src/index.js',
    output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
    },
    module: {
        rules: [
            //loader的配置
            {
                //处理css资源
                test: /\.css$/,
                use: [
                    //创建style标签,将样式插入
                    //'style-loader',
                    //这个loader取代style-loader。作用:提取js中的css成单独文件

                    MiniCssExtractPlugin.loader,

                    //将css文件整合到js文件中
                    'css-loader',

                    /*
                        css兼容性处理:postcss---->postcss-loader----->post-preset-env
                        帮postcss找到package.json中browserslist里的配置,通过配置加载指定的css兼容性样式
                    	
                        "borwserlist":{
                            "development":[
                                'last 1 chrome  version',
                                'last 1 firefox  version',
                                'last 1 safari  version',
                            ],
                            "production":[
                                '>0.2%',
                                'not dead',
                                'not op_mini all',
                            ]
                    	
                        }
                    */

                    //postcss-loader
                    {
                        loader: 'post-loader',
                        options: {
                            ident: 'postcss',
                            plugins: () => [//postcss 插件
                                require('post-preset-env')()
                            ]
                        }
                    }
                ]
            },
        ]
    },
    plugins: [
        //plugins的配置
        //处理html
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        //将css文件单独提出为一个文件
        new MiniCssExtractPlugin({
            //对输出的css文件进行重命名
            filename: 'css/built.css'
        }),
        //压缩css文件
        new OptimizeCssAssetsWebpackPlugin()
    ],
    mode: 'development'
}

js的处理

  • js语法检查: loader:eslint-loader plugin: eslint-config-airbin-base eslint eslint-plugin-import

  • js的兼容性处理:

    ​ 需要的loader: babel-loader @babel/core @babel/preset-env

    ​ 1.基本js兼容性处理—>@babel/preset-env 能解决

    ​ 问题:只能转换基本语法,如promise不能转换

    ​ 2.全部js兼容性处理—> @babel/polyfill

    ​ 问题:只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大

    ​ 3.需要做兼容性处理的就做:按需加载—>core-js

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.export = {
    entry: './src/index.js',
    output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
    },
    module: {
        rules: [
            /*
                语法检查:eslint-loader eslint
                注意:只检查自己写的源代码,第三方的库是不检查的
                设置检查规则:
                    配合package.json中的eslintConfig设置
                    package.json的eslintConfig设置
                    "eslintConfig":{
                        extend:"airbnb-base"
                    }

                  需要的插件:  airbnb--> eslint-config-airbnb-base --->eslint-plugin-import---> eslint 
                  需要的loader: eslint-loader

            */
            {
                test: /\.js$/,
                //排除对node_modules的检查
                exclude: /node_modules/,
                loader: 'eslint-loader',
                options: {
                    //自动修复
                    fix: true
                }

            },
            /*
                js 兼容性处理

                需要的loader: babel-loader  @babel/core  @babel/preset-env
                    1.基本js兼容性处理--->@babel/preset-env 能解决
                        问题:只能转换基本语法,如promise不能转换
                    2.全部js兼容性处理---> @babel/polyfill
                        问题:只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大
                    3.需要做兼容性处理的就做:按需加载--->core-js   

            */
            {
                test: /\.js$/,
                //排除对node_modules的检查
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                    //预设:指示babel做怎样的兼容性处理
                    presets: [
                        '@babel/preset-env',
                        {
                            useBuiltIns: 'usage',
                            //指定core-js版本
                            corejs: {
                                version: 3
                            },
                            //指定兼容性做到哪个版本浏览器
                            targets: {
                                chrome: '60',
                                firefox: '60',
                                ie: '9',
                                safari: '10',
                                edge: '17'
                            }
                        }

                    ]
                }
            }
        ]
    },
    plugins: [
        //plugins的配置
        //处理html
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
    ],
    mode: 'development'
}

js和html的压缩

  1. 生产环境自动会压缩js代码

    ​ 只需要修改mode配置为生产环境即可 mode:'production' 此时生产环境下的UglifyJsPlugin就会压缩js代码

  2. html代码的压缩是利用 html-webpacj-plugin 这个插件

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.export = {
    entry: './src/index.js',
    output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
    },
    module: {},
    plugins: [
        //plugins的配置
        //处理html
        new HtmlWebpackPlugin({
            template: './src/index.html',
            //压缩html的配置
            minify: {
                //移出空格
                collapseWhitespace: true,
                //移出注释
                removeComments: true
            }
        }),
    ],
    //生产环境自动会压缩js代码
    mode: 'production'
}

生产环境配置(代码)

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

//定义node.js环境变量,决定使用borwserslist的哪个环境
process.env.NODE_ENV = 'production'

//复用loader
const commonCssLoader = [
    //压缩css
    MiniCssExtractPlugin.loader,
    "css-loader",
    //css兼容  注意还需要再package.json中配合着browserlist使用
    {
        loader: 'postcss-loader',
        options: {
            ident: 'postcss',
            plugins: () => [
                require('postcss-preset-env')()
            ]
        }
    }

]

module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
    },
    module: {
        rules: [
            {
                test: /\.css&/,
                use: [...commonCssLoader],
            },
            {
                test: /\.less&/,
                use: [
                    ...commonCssLoader,
                    "less-loader",
                ]
            },

            /*
                正常来讲一个文件只能被一个loader处理
                当一个文件被多个loader处理,那么一定要指定loader执行的先后是顺序
                先执行eslint 再执行babel
            */

            {
                //js的语法检查 需要配合着package.json中的eslintConfig--->airbnb
                test: /\.js&/,
                //排除对node_modules的检查
                exclude: /node_modules/,
                loader: 'eslint-loader',
                enforce: 'pre',//优先执行
                options: {
                    //自动修复
                    fix: true
                }
            },
            //js的兼容性处理
            {
                test: /\.js$/,
                //排除对node_modules的检查
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                    //预设:指示babel做怎样的兼容性处理
                    presets: [
                        //只能做基本的兼容性处理
                        '@babel/preset-env',
                        //一开始是使用@babel/polyfill 这个库,但是会所有兼容性处理全部引进项目,增加项目体积,所以采用了下面的corejs这种方式
                        {
                            useBuiltIns: 'usage',
                            corejs: { version: 3 }, //指定core-js版本
                            //指定兼容性做到哪个版本浏览器
                            targets: {
                                chrome: '60',
                                firefox: '60',
                                ie: '9',
                                safari: '10',
                                edge: '17'
                            }
                        }

                    ]
                }
            },
            {
                //处理图片资源
                test: /\.(jpg|png|gif)$/,
                loader: 'url-loader',
                options: {
                    limit: 8 * 1024,
                    name: '[hash:10].[ext]'
                },
                //关闭es6模块化
                esMoudle: false
            },
            {
                //处理html中引入图片资源
                test: /\.html$/,
                loader: 'html-loader',
            },
            {
                //处理其他资源
                test: /\.(html|js|css|less|jpg|png|gif)$/,
                loader: 'file-loader',
                options: {
                    name: '[hash:10].[ext]',
                    outputPath: "media"
                }
            },
        ]
    },
    plugins: [
        //处理html
        new HtmlWebpackPlugin({
            template: './src/index.html',
            //压缩html的配置
            minify: {
                collapseWhitespace: true,  //移出空格
                removeComments: true   //移出注释
            }
        }),
        new MiniCssExtractPlugin({
            filename: "css/built.css"
        }),
        //压缩css
        new OptimizeCssAssetsWebpackPlugin()
    ],
    mode: 'production'
}

webpack 性能优化

优化的内容:开发环境的优化 和 生产环境的优化

开发环境的优化:

  • 优化打包构建速度
  • 优化代码调试

生产环境的优化

  • 优化打包构建速度
  • 优化代码运行的性能

开发环境的优化

打包构建速度的优化

HMR:hot module replacement 热模块替换/模块热替换

作用:一个模块的发生变化,只会重新打包这一个模块(而不是打包所有的模块),极大提升了构建速度

devServer:{
    contentBase:resolve(__dirname,'build'),
    compress:true,
    port:3000,
    open:true,
    //开启热模块替换
    hot:true
}

HRM对不同文件处理的结果

样式文件

​ 可以使用HRM功能:因为style-loader内部实现

js文件

​ js文件默认是没有HRM的功能 —>需要修改js代码,添加支持HRM功能的代码

注意:HRM功能对js文件的处理,只能处理非入口js文件的其他文件,因为如果是入口文件做了HRM功能,那么只要入口文件发生变化,其他的js文件都会发生变化,因为其他文件再入口文件中都有引入

import print from './print'
console.log('index.js文件被加载了')
print()
function add(x,y){
    return x+y
}
console.log(1,2)
if(moudle.hot){
    //一旦moudle.hot 为true,说明开启了HRM功能,---->让HRM功能代码生效
    moudle.hot.accept('./print.js',function(){
        //方法会监听print.js 文件的变化,一旦发生变化,其他默认不会重新打包构建
        //会执行后面的回调函数
        print()
    })
}

html文件

​ html文件默认是没有HRM功能,同时会导致问题:html文件不能热更新了(不用做HRM功能)

​ 解决:修改entry入口,将html文件引入

entry:['/src/js/index.js','./src/index.html']

但是这么解决还是一个问题:就是html一更新,所有的文件都会重新加载一边,还是没办法做到HRM。

因为html文件只有一个,所以当html文件发生变化的时候,它肯定是要重新夹杂的,所以,html文件时不需要做HRM功能的

调试代码的优化

source-map:一种提供源代码到构建后代码映射技术 (如果构建后代码出错,通过映射可以追踪源代码错误)

先说总结: 开发环境使用eval-source-map 生产环境使用:source-map

配置devtool 属性为true

devServer:{
    contentBase:reslove(__dirname,'build'),
    compress:true,
    port:3000,
    open:true,
    hot:true
},
    
devtool:'source-map'

source-map 类型

[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

  • source-map:外部

    错误代码准确信息 和 源代码的错误位置都能准确提示

  • inline-source-map: 内联(只生成一个source-map)

    ​ 错误代码准确信息 和 源代码的错误位置都能准确提示(和source-map 基本一致)

  • hidden-source-map:外部

    ​ 错误代码错误原因可以提示到,但是没有错误位置

    ​ 不能追踪到源代码的错误位置,只能提示到构建后代码的错误位置

  • eval-source-map:内联(每一个文件都生成对应的source-map,都在eval)

    ​ 每一个文件都生成对应的source-map,都再eval

    ​ 错误代码准确信息 和 源代码的错误位置

  • nosources-source-map:外部

    ​ 错误代码准确信息 ,但是没有任何源代码的信息

  • cheap-module-source-map:外部

    ​ 错误代码准确信息 和 源代码的错误位置

    ​ 只能精确到行

  • cheap-module-source-map:外部

    ​ 错误代码准确信息 和 源代码的错误位置

    ​ module会将loader的source map 加入

内联和外部的区别

  1. 外部生成了文件,内联没有
  2. 内联构建速度更快

开发环境:速度快,调式优化

​ 速度快:(eval>inline>cheap>…)

​ eval-cheap-source-map

​ eval-source-map

​ 调试友好:

​ source-map

​ cheap-module-source-map

​ cheap-source-map

综合选取: eval-source-map / eval-cheap-module-source-map

生产环境:源代码是否需要隐藏?调试要不要友好?

内联会让代码体积变大,随意再生产环境不用内联

nosources-source-map 源代码和构建代码 全部隐藏

hidden-source-map 只隐藏 源代码,会提示构建后代码提示错误

综合选取: source-map / cheap-module-source-map

生产环境的优化

oneof

正常来讲一个文件只能被一个loader处理。但在执行的时候,该文件会将loader中的所有ruler都过一遍,如果符合,则被对应的loader处理,如果不符合就直接通过,这样对构建的性能很不友好,所有可以用oneof。oneof可以在遇到第一个与之对应的loader之后,不会再往下进行。

oneOf里面的loader只匹配一个。不能有两个配置处理同一种类型的文件

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

//定义node.js环境变量,决定使用borwserslist的哪个环境
process.env.NODE_ENV = 'production'

//复用loader
const commonCssLoader = [
    //压缩css
    MiniCssExtractPlugin.loader,
    "css-loader",
    //css兼容  注意还需要再package.json中配合着browserlist使用
    {
        loader: 'postcss-loader',
        options: {
            ident: 'postcss',
            plugins: () => [
                require('postcss-preset-env')()
            ]
        }
    }

]

module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
    },
    module: {
        rules: [
            {   /*
                     正常来讲一个文件只能被一个loader处理
                     当一个文件被多个loader处理,那么一定要指定loader执行的先后是顺序
                     先执行eslint 再执行babel
                 */

                //js的语法检查 需要配合着package.json中的eslintConfig--->airbnb
                test: /\.js&/,
                //排除对node_modules的检查
                exclude: /node_modules/,
                loader: 'eslint-loader',
                enforce: 'pre',//优先执行
                options: {
                    //自动修复
                    fix: true
                }
            },
            {
                oneof: [

                    {
                        test: /\.css&/,
                        use: [...commonCssLoader],
                    },
                    {
                        test: /\.less&/,
                        use: [
                            ...commonCssLoader,
                            "less-loader",
                        ]
                    },


                    //js的兼容性处理
                    {
                        test: /\.js$/,
                        //排除对node_modules的检查
                        exclude: /node_modules/,
                        loader: 'babel-loader',
                        options: {
                            //预设:指示babel做怎样的兼容性处理
                            presets: [
                                //只能做基本的兼容性处理
                                '@babel/preset-env',
                                //一开始是使用@babel/polyfill 这个库,但是会所有兼容性处理全部引进项目,增加项目体积,所以采用了下面的corejs这种方式
                                {
                                    useBuiltIns: 'usage',
                                    corejs: { version: 3 }, //指定core-js版本
                                    //指定兼容性做到哪个版本浏览器
                                    targets: {
                                        chrome: '60',
                                        firefox: '60',
                                        ie: '9',
                                        safari: '10',
                                        edge: '17'
                                    }
                                }

                            ]
                        }
                    },
                    {
                        //处理图片资源
                        test: /\.(jpg|png|gif)$/,
                        loader: 'url-loader',
                        options: {
                            limit: 8 * 1024,
                            name: '[hash:10].[ext]'
                        },
                        //关闭es6模块化
                        esMoudle: false
                    },
                    {
                        //处理html中引入图片资源
                        test: /\.html$/,
                        loader: 'html-loader',
                    },
                    {
                        //处理其他资源
                        test: /\.(html|js|css|less|jpg|png|gif)$/,
                        loader: 'file-loader',
                        options: {
                            name: '[hash:10].[ext]',
                            outputPath: "media"
                        }
                    },
                ]
            }
        ]
    },
    plugins: [
        //处理html
        new HtmlWebpackPlugin({
            template: './src/index.html',
            //压缩html的配置
            minify: {
                collapseWhitespace: true,  //移出空格
                removeComments: true   //移出注释
            }
        }),
        new MiniCssExtractPlugin({
            filename: "css/built.css"
        }),
        //压缩css
        new OptimizeCssAssetsWebpackPlugin()
    ],
    mode: 'production'
}

缓存

babel缓存

使用cacheDirectory :true 第二次构建是会读取之前的缓存

文件资源缓存

文件资源缓存其实就是修改文件名,防止文件再http的强制缓存中读取,文件资源已经改了,但是文件没有重新从后台拿取

hash:每次webpack构建时,都会生成一个唯一hash值

​ 问题:因为js 和 css同时使用一个hash值(webpack生成的hash),如果重新打包,会导致所有的缓存都失效(可能只改了一个文件)

chunkhash:根据chunk生成的hash值。如过打包来源于同一个chunk,那么hash就是一样的

​ 问题: js 和 css 还是同一个hash 值。(因为css是在js中被引入的,所以同属于一个chunk)

contenthash:根据文件内容生成hash值。不同文件hash一定不一样

tree shaking (树摇)

前提:

  1. 必须使用Es6 模块化
  2. 开启production环境

作用:减少代码的体积

问题:可能会把css也树摇掉

方案:在package.json 设置sideEffects:[‘*.css’]

sideEffects:['*css','*less']

code split (代码分割)

1. 多入口

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

process.env.NODE_ENV = 'production'
module.exports = {
    //单入口
    // entry: './src/js/index.js',
    //多入口:有一个入口,最终输出就有一个bundle
    entry: {
        main: "./src/js/index.js",
        test: "./src/js/test.js"

    },
    output: {
        //[name]:取文件名
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            minify: {
                collapseWhitespace: true,
                removeComments: true
            }
        }),
    ],
    mode: 'production'
}

2. optimization [ˌɒptɪmaɪˈzeɪʃən]配置

  1. 可以将node_modules中的代码单独打包成一个chunk,最终输出
  2. 同时在多入口模式下,可以自动分析多入口的chunk中,有没有公共文件,如果有,会打包成单独的一个chunk
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

process.env.NODE_ENV = 'production'
module.exports = {
    //单入口
    entry: './src/js/index.js',
    output: {
        //[name]:取文件名
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            minify: {
                collapseWhitespace: true,
                removeComments: true
            }
        }),
    ],
    /*
        可以将node_modules中的代码单独打包成一个chunk,最终输出
        同时在多入口模式下,可以自动分析多入口的chunk中,有没有公共文件,如果有,会打包成单独的一个chunk
    */
    optimization: {
        splitChunks: {
            chunks: 'all'
        }
    },
    mode: 'production'
}

3. 通过js代码,让某一个文件被单独打包成一个chunk

利用import 动态导入语法:能将某个文件单独打包

import (/* webpackChunkName: 'test'*/'.test').then((result)=>{
    //文件加载成功
    console.log(result)
})
.cathch(()=>{
    console.log('文件加载失败')
})

懒加载与预加载

懒加载和预加载都是通过自己写代码实现

懒加载

当文件需要使用的时候才加载

console.log('index.js 文件被加载了')
document.getElementById('btn').onClick = function () {
    //懒加载
    import('./test').then(({ mul }) => {
        console.log(mul(4, 5))
    })
}

预加载 prefetch

会在使用之前,提前加载js文件

console.log('index.js 文件被加载了')
document.getElementById('btn').onClick = function () {
    //懒加载
    impor(/* webpackChunkName: 'test',webpackPrefetch:true */'./test').then(({ mul }) => {
        console.log(mul(4, 5))
    })
}

PWA

渐进式网络开发应用程序(离线可访问)

利用workbox 插件:workbox-webpack-plugin

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')

process.env.NODE_ENV = 'production'
module.exports = {
    //单入口
    entry: './src/js/index.js',
    output: {
        //[name]:取文件名
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            minify: {
                collapseWhitespace: true,
                removeComments: true
            }
        }),
        new WorkboxWebpackPlugin.GenerateSW({
            /*
                1.帮助serviceworker快速启动
                2. 删除就的serviceworker 

                生成一个serviceworker配置文件
            */
            clientClaim: true,
            skipWaiting: true
        })
    ],
    /*
        可以将node_modules中的代码单独打包成一个chunk,最终输出
        同时在多入口模式下,可以自动分析多入口的chunk中,有没有公共文件,如果有,会打包成单独的一个chunk
    */
    optimization: {
        splitChunks: {
            chunks: 'all'
        }
    },
    mode: 'production'
}

同时需要在入口文件中注册serviceworker

if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceworker
            .register('/service-worker.js')
            .then(() => {
                console.log('sw注册成功了')
            })
            .catch(() => {
                console.log('sw注册失败了')
            })
    })
}

在入口文件这么写可能会有问题:

  1. eslint 不认识 window 、navigator全部变量

    解决:需要修改package.json中eslintConfig配置

"eslintConfig":{
    "extends":"airbnb-base",
    "env":{
        "browser":true //支持浏览器全局变量
    }
}
  1. sw代码必须运行在服务器上

多线程打包

使用thread-loader 给babel用

多线程打包有利有弊,进程开启时间大概为600ms,进程通信也有开销

只有工作消耗时间比较长的,才需要多进程打包

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

//定义node.js环境变量,决定使用borwserslist的哪个环境
process.env.NODE_ENV = 'production'

//复用loader
const commonCssLoader = [
    //压缩css
    MiniCssExtractPlugin.loader,
    "css-loader",
    //css兼容  注意还需要再package.json中配合着browserlist使用
    {
        loader: 'postcss-loader',
        options: {
            ident: 'postcss',
            plugins: () => [
                require('postcss-preset-env')()
            ]
        }
    }

]

module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
    },
    module: {
        rules: [
            {   /*
                     正常来讲一个文件只能被一个loader处理
                     当一个文件被多个loader处理,那么一定要指定loader执行的先后是顺序
                     先执行eslint 再执行babel
                 */

                //js的语法检查 需要配合着package.json中的eslintConfig--->airbnb
                test: /\.js&/,
                //排除对node_modules的检查
                exclude: /node_modules/,
                loader: 'eslint-loader',
                enforce: 'pre',//优先执行
                options: {
                    //自动修复
                    fix: true
                }
            },
            {
                oneof: [

                    {
                        test: /\.css&/,
                        use: [...commonCssLoader],
                    },
                    {
                        test: /\.less&/,
                        use: [
                            ...commonCssLoader,
                            "less-loader",
                        ]
                    },


                    //js的兼容性处理
                    {
                        test: /\.js$/,
                        //排除对node_modules的检查
                        exclude: /node_modules/,
                        use: [
                            {
                               loader:'thread-loader'options:{
                                worker:2 //进程2个
                            }
                            },
                            {
                                loader: 'babel-loader',
                                options: {
                                    //预设:指示babel做怎样的兼容性处理
                                    presets: [
                                        //只能做基本的兼容性处理
                                        '@babel/preset-env',
                                        //一开始是使用@babel/polyfill 这个库,但是会所有兼容性处理全部引进项目,增加项目体积,所以采用了下面的corejs这种方式
                                        {
                                            useBuiltIns: 'usage',
                                            corejs: { version: 3 }, //指定core-js版本
                                            //指定兼容性做到哪个版本浏览器
                                            targets: {
                                                chrome: '60',
                                                firefox: '60',
                                                ie: '9',
                                                safari: '10',
                                                edge: '17'
                                            }
                                        }

                                    ]
                                }
                            },
                        ],

                    },
                    {
                        //处理图片资源
                        test: /\.(jpg|png|gif)$/,
                        loader: 'url-loader',
                        options: {
                            limit: 8 * 1024,
                            name: '[hash:10].[ext]'
                        },
                        //关闭es6模块化
                        esMoudle: false
                    },
                    {
                        //处理html中引入图片资源
                        test: /\.html$/,
                        loader: 'html-loader',
                    },
                    {
                        //处理其他资源
                        test: /\.(html|js|css|less|jpg|png|gif)$/,
                        loader: 'file-loader',
                        options: {
                            name: '[hash:10].[ext]',
                            outputPath: "media"
                        }
                    },
                ]
            }
        ]
    },
    plugins: [
        //处理html
        new HtmlWebpackPlugin({
            template: './src/index.html',
            //压缩html的配置
            minify: {
                collapseWhitespace: true,  //移出空格
                removeComments: true   //移出注释
            }
        }),
        new MiniCssExtractPlugin({
            filename: "css/built.css"
        }),
        //压缩css
        new OptimizeCssAssetsWebpackPlugin()
    ],
    mode: 'production'
}

externals

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时再去从外部获取这些扩展依赖(external dependencies)

一般配合着CDN资源使用

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

//定义node.js环境变量,决定使用borwserslist的哪个环境
process.env.NODE_ENV = 'production'

module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
    },
    plugins: [
        //处理html
        new HtmlWebpackPlugin({
            template: './src/index.html',
        }),
    ],
    mode: 'production',
    externals: {
        // 拒绝jQuery 被打包进来
        jquery: 'jQuery'
    }
}

dll

使用dll 技术,对某些库(第三方库:jquery、vue、react…)进行单独打包

webpack.dll.js 文件

const webpack  = require('webpack')
moudle.export ={
    entry:{
        //最终打包生成的[name]----->jquery
        //['jquery'] ----> 要打包的库是jquey
        jquery:['jquery']
    },
    output:{
		filename:'[name].js',
        path:resolve(__dirname,'dll'),
        library:['[name]_[hash]'],//打包的库里面向外暴露出去的内容叫什么名字
    },
    plugins:[
        //打包生成一个mainfest.json --> 提供和jquery映射
        new webpack.Dllplugin({
            name:'[name]_[hash]',//映射库的暴露内容名称
            path:resolve(__dirname,'dll/mainfest.json') //输出文件路径
        })
    ]
    mode:'production'
}

webpack.config.js文件

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const AddAssetHtmlWebpackPlugin = require('add-assetHtml-webpack-plugin')
//定义node.js环境变量,决定使用borwserslist的哪个环境
process.env.NODE_ENV = 'production'

module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
    },
    plugins: [
        //处理html
        new HtmlWebpackPlugin({
            template: './src/index.html',
        }),
        //告诉webpack 哪些库不参与打包,同时使用是的名称也得变
        new webpack.DllReferencePlugin({
            manifest: resolve(__dirname, 'dll/manifest.json')
        }),
        //将某个文件打包输出去,并在html中自动引入该资源
        new AddAssetHtmlWebpackPlugin({
            filePath: resolve(__dirname, 'dll/jquery.js')
        })
    ],
    mode: 'production',
    externals: {
        // 拒绝jQuery 被打包进来
        jquery: 'jQuery'
    }
}

需要知道的webpack知识

1. 启动gzip压缩

在devserver中,配置compress:true

2. css的兼容性处理

需要利用postcss-loader 和 postcss-preset-env 配合着package.json中的browserslist 来实现

3.优化开发环境的打包构建速度-HRM

css的HRM:无须处理,因为style-loader内部已经处理过了

js的HRM:·只能处理非入口文件的js文件的HRM,需要添加能够实现HRM功能的js代码

if(moudle.hot){
    moudle.accept('./文件名.js',function(){
        //相应的回调函数
    })
}

html的HRM:无需做

4.开发环境下的代码调试

使用source-map技术,在开发环境下使用eval-source-map (速度快,调试友好) ,在生产环境下使用source-map(调试友好)

5.生成环境下的性能优化

  • oneof

  • 缓存

    • babel缓存
    • 文件资源缓存
      • hash
      • chunkhash
      • contenthash
  • tree shaking

    ​ 作用:减少代码体积,

    ​ 前提:开启了es6模块化,必须时production环境

    ​ 可能会对css文件进行树摇 ,解决方案就是在package.json中设置sideEffects:['*.css','*.less']

  • code split

    • 多入口文件 配置entry为一个对象 ,同时给出口文件的filename 里 添加 [name]属性
    • 配置optimization,将node_modules单独打包成一个chunk ,或者在多入口文件,使用这个属性,可以加公共文件打包成一chunk
    • 使用import 动态导入语法
  • 懒加载和预加载

    ​ 其实懒加载和预加载都是使用import语法来实现的

  • externals

    ​ 配合着CDN资源使用,将一些包不打到bundle中

  • dll

    需要打包一次,以后就不打包了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值