webpack 成长指南

307 阅读4分钟

webpack配置相关

  • webpack默认支持commonjs规范和esmodules规范
  • webpack-cli可以解析用户传递的参数
  • 浏览器无法识别require(),module.exports,所以需要webpack打包解析

webpack webpack-cli 0配置的方式打包

执行方式:

  • 第一种:当前目录下:npx webpack(npx 可以执行当前node_modules/.bin目录下的文件;两个模式: 开发模式,生产模式;npx webpack --mode development 开发模式下打包出的文件不会被压缩,生产模式会压缩和优化)
  • 第二种:在package.json文件配置
   "scripts":{
      "dev":"webpack --mode development",
      "bulid":"webpack --mode production"
   }

执行npm run build时,会暂时将当前node_modules/.bin目录下的文件放在全局环境变量下,执行完销毁

webpack配置文件

配置文件默认名字:webpack.config.jswebpack.file.js

package.json文件

   "scripts":{
       "dev:build":"webpack --env.development --config ./build/webpack.base.js",
       "dev":"webpack-dev-server --env.development --config ./build/webpack.base.js",
       "bulid":"webpack --env.production --config ./build/webpack.base.js",
       "dll":"webpack --config webpack.dll.js"
    },
    /*
    可以通过--config 指定配置的文件是哪一个①默认引用base传入模式,②分别引入dev,prod,在特定地方base
    {
        env: { development: true },
        config: './build/webpack.base.js',
    }
    webpack-dev-server是在内存中打包的,不会产生实体文件
    */
    "sideEffects":[
        "**/*.css"
    ],
    //移除副作用(引入变量未使用,css后缀的不删除)

build/webpack.base.js文件

// webpack是基于nodejs,语法commonjs规范
//一般情况下,我们分成三个模式 一个是开发模式,一个是生产模式,还有一个基本配置模式
const dev = require('./webpack.dev');
const prod = require('./webpack.prod');
const path = require('path');
const merge = require('webpack-merge');//合并配置
const HtmlWebpackPlugin = require('webpack-html-plugin');//将html文件打包[在src下建public/index.html文件]
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const glob = require('glob');//主要功能就是查找匹配的文件
let paths = glob.sync("./src/**/*",{nodir:true});//src下任意文件夹下任意文件,nodir不找文件
//主要的作用删除无意义的css,只能配合mini-css-extract-plugin使用
const PurgeCssWebpackPlugin = require('purgecss-webpack-plugin');
const AddCdnPlugin = require('add-asset-html-cdn-webpack-plugin');
const DllReferencePlugin = require('webpack').DllReferencePlugin;
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
const {BundleAnalyzerPlugin} =require('webpack-bundle-analyzer');//图像化大小查看
const smw = require('speed-measure-webpack-plugin');//费时分析
//用 smw.wrap({})把base包起来
module.exports = (env) =>{ //env是环境变量 {development:true}
    let isDev = env.development;
    const base = {
        entry:{ //多入口
            "a":"src/a.js",
            "b":"src/b.js"
        },
        optimization:{
            splitChunks:{
                
            }
        }
        //入口,出口
        //entry:path.resolve(__dirname,'../src/index.js'),//写路径都采用绝对路径(减少可能的修改)
        output:{
            fliename:'[name].js', //多出口
            //filename:'bundle.js',//同步打包的名字
            chunkFilename:'[name].min.js',//异步打包的名字
            path:path.resolve(__dirname,'../dist') //出口必须是绝对路径
        }, 
        externals:{ // 外部资源文件
            'jquery':'$', //不去打包代码中的jquery
            
        },
        module:{
            //转化什么文件,用什么去转,使用哪些loader
            //loader 写法 []  {} ''
            
            //解析css的时候,不能渲染dom了
            //css可以并行和js一同加载 mini-css-extract-plugin
            rules:[
                /*
                {
                    test: /\.(j|t)sx?$/,
                    loader: "ts-loader",
                    exclude:/node_modules/
                },
                {
                    enforce: "pre",
                    test: /\.js$/,
                    loader: "source-map-loader"
                },
                */
                {
                    test:/\.vue$/,
                    use:'vue-loader'
                    /*
                    vue 使用 typescript
                    需要使用垫片,即增加vue-shims.d.ts文件
                    declare module "*.vue"{
                        import Vue from 'vue';
                        export default Vue;
                    }
                    */
                },
                { //解析js文件,babel-loader默认会调用@babel/core,@babel-core会调用.babelrc文件
                    test:/\.js$/,
                    use:'babel-loader'
                    /*参数太多不方便配置,可以增加.babelrc文件,默认调用
                        module.exports ={
                            "presets":[ //从下到上执行
                                "@babel/preset-env",{
                                    //使用的api会自动转化,并且是按需加载
                                    "usageBuitIns":"usage",
                                    //和babel-polyfill 一样
                                    "corejs":2
                                },
                                "@babel/preset-react",
                                "@babel/preset-typescript",{
                                    "allExtentions":true //匹配所有的.vue文件
                                }
                            ],
                            "plugins":[
                                ["@babel/plugin-proposal-decorators",{"legacy":true}],//保存装饰器语法 ["@babel/plugin-proposal-class-properties",{"loose":true}],//宽松模式this.a=a;才可以使用装饰器;严格模式使用defineProperty,不能使用装饰器
                                ["@babel/plugin-transform-runtime"] //整合重复的代码,例如classCallback
                            ]
                        }
                    */
                },
                {
                    test:/\.css$/,
                    use:[ //生产环境再抽离 link
                    isDev?'style-loader':MiniCssExtractPlugin.loader,
                    {
                        loader:'css-loader?modules', //启用css modules 即styles.aa格式
                        options:{ //给loader传递参数
                            importLoaders:2 //如果css文件引入了其他文件@import
                        }
                    },'postcss-loader','sass-loader'] //postcss-loader对css进行处理
                    /*
                    需要增加文件 postcss.config.js 
                        module.exports = {
                            plugins:[require('autoprefixer')] //自动添加前缀
                        }
                    需要增加文件 .browserslistrc
                        cover 95%
                    */
                },
                { //匹配到scss结尾的使用sass-loader 来调用node-sass处理sass文件
                    test:/\.scss$/,
                    use:['style-loader','css-loader','sass-loader']
                },
                { //图标的转化
                    test:/\.(woff|ttf|eot|svg)$/,
                    use:'file-loader'
                },
                { //图片的转化  
                    test:/\.(jpe?g|png|gif)$/,
                    use:[
                    {
                        loader:'file-loader',
                    },
                    !isDev&&{ //可以在使用file-loader之前,对图片进行降清晰度压缩
                        loader:'img-webpack-loader',
                        options:{
                            //...
                        }
                    },
                    {
                    loader:'url-loader',
                    //如果大于10k,会使用file-loader
                    options:{
                        name:"image/[contentHash].[ext]",
                        limit:10*1024
                    }
                    }//'file-loader' //file-laoder 默认的功能是拷贝的作用
                     //比较小的图片可以转化成base64比以前大,好处是不用发送http请求
                    ].filter(Boolean)
                }
            ]
        },
        plugins:[
            !isDev&&new MiniCssExtractPlugin({ //如果是开发模式,就不要使用抽离插件
                filename:'css/main.css'
            }),
            new VueLoaderPlugin(),
            /*
            new HtmlWebpackPlugin({
                template:path.resolve(__dirname,'../public/index.html'),
                filename:'index.html',
                minify:!isDev && { //在开发模式压缩index.html
                    removeAttributeQuotes:true,
                    collapseWhitespace:true
                }
            }),
            */
             new HtmlWebpackPlugin({
                template:path.resolve(__dirname,'../public/index.html'),
                filename:'index.html',
                chunks:['a']
            }),
             new HtmlWebpackPlugin({
                template:path.resolve(__dirname,'../public/login.html'),
                filename:'login.html',
                chunksSortMode:'manual',//手动按照我的顺序排序
                chunks:['b','a'] //打包的顺序,按照自己的排序
            }),
            new PurgeCssWebpackPlugin({
                paths
            }),
            new DllReferencePlugin({
                manifest:path.resolve(__dirname,'dll/manifest.json')
            }),
            //将dll加到html中 
            new AddAssetHtmlPlugin({
               filepath:path.resolve(__dirname,'./dll/react.dll.js') 
            }),
            !isDev&&new BundleAnalyzerPlugin(),
            //当前这个dll.js没有在页面引用
            //添加cdn的插件
            //分割代码
            new AddCdnPlugin(true,{ //第一个参数,boolean值,是否开启工作
                'jquery':'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js'
            })
        ].filter(Boolean)
    }
    //函数返回配置文件,没返回采用默认配置
    if(isDev){
        return merge(base,dev);
    }else{
        return merge(base,prod);
    }
    
}

build/webpack.dev.js文件

module.exports = {
     mode:'development',//当前是开发模式(等同于package.json的 "dev":"webpack --mode development")
     devServer:{ //开发服务的配置
         port:3000,
         compress:true,//gzip 可以提升返回页面的速度
         contentBase:path.resolve(__dirname,'../dist');//webpack启动服务会在dist目录下
     }
}

build/webpack.prod.js文件

const {CleanWebpackPlugin} = require('clean-webpack-plugin');//清除打包的文件
const OptimizeCssAssetsPlugin = require('optimize-css-assets-wabpack-plugin');
const TerserWebpackPlugin= require('terser-webpack-plugin');
module.exports = {
    mode:'production',//当前是生产模式
    optimization:{ //优化项 (生产环境的css没办法自动压缩)
        minimizer:[ //可以放置压缩方案
            new OptimizeCssAssetsPlugin(),//用了这个 js也得手动压缩
            new TerserWebpackPlugin() //压缩js
        ]    
    },
    plugins:[
            new CleanWebpackPlugin(),//在打包之前清空dist目录下文件,可以传参选择清空某些文件
        ]
}

webpack.dll.js

const path = require('path');
const DLLPlugin = require('webpack');

//需要产生一个缓存列表
module.exports={
    mode:'development',
    entry:['react','react-dom'], //第三方模块的react,react-dom打包
    output:{
        library:'react', //打包后接收只执行函数的名字叫calc
        //libraryTarget:'commonjs2',默认用var 模式 (commonjs,commonjs2 )
        filename:'react.dll.js',
        path:path.resolve(__dirname,'dll')
    },
    plugins:[
        new DLLPlugin({
            name:'react',
            path:path.resolve(__dirname,'dll/manifest.json')
        }),
        
    ]
    
}

//目前是为了将calc打包成node可以使用的模块

note.md

css-loader 会解析css语法,style-loader会将解析的css变成style标签,插入到页面中
loader的执行顺序,默认是从下到上执行,从右到左执行
预处理器 .scss   node-sass sass-loader
         .less   less      less-loader
         .stylus stylus  styles-loader
         
默认会调用@babel/core 转化代码,转化的时候需要用@babel/preset-env转化成es5
@babel/core @babel/preset-env babel-loader

webpack优化

优化css,删除无用css
purgecss-webpack-plugin glob

降低图片分辨率,需要和file-loader一起用
img-webpack-loader

引用外部cdn文件
 externals:{ // 外部资源文件
    'jquery':'$', //不去打包代码中的jquery 
},
添加cdn的插件
new AddCdnPlugin(true,{ //第一个参数,boolean值,是否开启工作
    'jquery':'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js'
})

tree-shaking 默认只支持es6 modules语法(静态导入),只在生产环境下有用
"sideEffects":[
    "**/*.css"
],
//移除副作用(引入变量未使用,css后缀的不删除)

每个模块都是一个函数,会导致内存过大
scope hoisting

dllPlugin动态链接库(一般用到开发环境上)
react+react-dom先打包好,放在那
本地使用了import react语法,需要先去manifest.json查找(基于Dllreferenceplugin),找到后会加载对应的库的名字,可能会引用某个模块,会去dll.js文件中查找
dll功能在开发之前,就先抽离好,打好包,以后就不用打包了


动态加载文件
动态导入,类比路由的懒加载,import语法
会使用jsonp动态加载calc文件
import可以实现代码分割
button.addEventListen('click',function(){
    import('/* webpackChunkName:'video' */' './calc').then(data=>console.log(data.add(1,2)))
})

实现多入口
index.html需要a.js ;login.html需要b.js
BundleAnalyzerPlugin

抽离第三方模块
1)不和业务逻辑放在一起
2)增加缓存 304
optimization