webpack成神(2)


2019-3-26 webpack

webpack 环境配置

使用环境变量

在开发环境 使用 dev 上线环境使用 production 测试环境使用 test,可以使用 webpack 自带插件 webpack.DeffinePlugin

```javascript
plugins: [new webpack.DefinePlugin(
    # 第一种 方法
    DEV:"'dev'"//要用双引号  内部嵌套一个单引号,因为如果只写一个引号相当于 console。log(dev)这个时候dev 是未定义的
    # 第二种方法
    DEV:JSON.stringify('production'),//输出字符串
    FLAG:'true',//输出boolean  的true
    EXPORESSION:'1+1' //输出2//在内部可以直接使用这些值
)]
```

区分环境变量

创建三个配置文件,webpack.base.js webpack.production.js webpack.development.js

使用 webpack-merge

    ```javascript
    # 安装
    npm i webpack-merge -D
    # 在开发环境中使用
    let {smart}  = require('webpack-merge')
    let base = require('./webpack.base.js')
    module.export=smart(base,{//集成了base中的所有配置 并且把mode 环境配置更改为development
        mode:'development',
        devserver,
        devtool...
    })
    # 在生产环境中使用
     let {smart}  = require('webpack-merge')
    let base = require('./webpack.base.js')
    module.export=smart(base,{//集成了base中的所有配置 并且把mode 环境配置更改为development
        mode:'production',
        minimizer...
    })
    ```

webpack 的优化

webpack 的忽略解析

配置不去解析特定包,看到包就忽略掉 在 module 选项下 配置noParse:/jquery/,这样可以根据通配符

找到 jquery 而不去解析 jquery 中的依赖项

配置 js 时默认会默认解析 node-modules

这个时候需要配置 babel 的排除和包含关系,排除 exclude 包含 include

```javascript
{
    test:/\.js/,
    exclude:/node_modules/,//排除node-modules 目录
    include:path.resolve('src'),//包含 src目录,意思时只在src 中进行寻找
    use:{
        loader:'babel-loader',
        options:{
            presets:[
                '@babel/preset-env',
                '@babel/preset-react'
            ]
        }
    }
}
```

关于某些库的优化

忽略掉第三方库内引用了却在实际开发中没有使用的代码块,使用 webpack.IgnorPlugin()

```javascript
# 使用
plugins:[
    new webpack.IgnorePlugin(/\./locale/,/monent/)//如果在moment中引用了。locale 这个文件夹 就忽略掉这个打包请求
]
# 自己手动引用忽略的包 这样就可以减小打包体积
```

动态链接库

如果在开发过程中需要某些第三方的库,这些库在后期过程中时根本不会产生变动并且这些库又很大,所以这分割时候

可以在打包的过程中先抽离打包出这个库,然后把这些库作为后续开发的依赖引入。

```javascript
# 新建配置文件webpack.config.react.js,这个配置文件作为打包第三方库依赖的配置文件
let path = require('path')
const webpack =require('webpack')
module.export={
    mdoe:'development',
    extry:{
        react:['react','react-dom']
    }
    output:{
        filename:'_dll_[name].js',//打包生成第三方库的文件名称
        path:path.resolve(__dirname,'dist'),//生成路径
        library:'_dll_[name]',//生成第三方模块的入口变量名称
        libaryTarget:'var'//caonst commenjs ... 生成第三方库的导出方式  一般是使用var 默认不写是var

    },
    plugins:[
        new webpack.DLLPlugin({
            name:'_dll_[name]',//这个名字和libary  名字要相同,打包出依赖的第三方库
            path:path.resolve(__dirname,'dist','manifest.json')//生成第三方库 在我们些的js 中引用和依赖的地图
        })
    ]
}
//根据上面文件打包会出现一个 manifest.json 关系对应地图 和一个_dll_react.js的文件
//使用上面的文件  在html  中引入 ./_dll_react.js
//然后再wenpack。config。js  中指定如果在打包过程中华又依赖关系到react  或者react-dom  那么去动态链接库中查找  而不是重新对node-modules  打包
//
plugins:{
    new webpack.DLLReferencePlugin({
        manifest:path.resolve(__dirname,'dist','minifest.json') //引用重新打包的依赖地图
    })
}

```

多线程打包实现快速打包

当项目比较大的时候使用多线程打包会加快速度 ,

使用 happypack

```javascript
//安装
npm i happypack -D
//使用
const HappyPackPlugin = reuqire('happypack')
//在module中需要使用多线程快速打包的地方
module:{
    noParse:/jquery/,
    rules:[
        {
            test:/\.js$/,
            exclude:/node_modules/,
            use:'HappyPackPlugin/loader?id=js'
        },
        {
            test:/\.css$/,
            use:'HappyPackPlugin/loader?id=css'
        }
    ]
},
//在plugins  中指定happypack

plugins:[
    new HappyPackPlugin({//通过id=js 找到上面的use  然后是u用happy 中定义的规则解析器进行打包
        id:'js',
        use:[
            {
                loader:'babel-loader',
                options:{
                    presets:[
                        '@babel/preset-env',
                        '@babel/preset-react'
                    ]
                }
            }
        ]
    }),
    new HappyPackPlugin({//通过id=css 找到上面的use  然后是u用happy 中定义的规则解析器进行打包
        id:'css',
        use:[
            {
                loader:MinicssWebpackPlugin.loader,
                options:{
                }
            },
            'css-loader',
            'postcss-loader'
        ]
    })
]
```

webpack 中的自带优化打包,

- webpack 在打包的时候默认在生产环境下会去除掉 import 却没有使用的代码块,而在开发环境下不会,这种叫做 tree-shaking
  把没用到的代码自动删除掉,而如果使用 require 打包的时候会把没用到的代码也打包,也就是 require 不支持 tree-shaking
- scope hosting 在 webpack 中会自动省略一些可以简化的代码

抽取公用代码

多用于多入口,与动态链接库有很大的差别,动态链接库最好不要使用与线上环境和整体性比较差的库。

```javascript
// 在多入口多界面的时候如果 A 界面和B 界面又公用的部分  我们希望把他们单独抽取出来这样在A页面加载的时候加载出来 在B页面就不要再进行加载了
//首先配置多入口
module.exports = {
  mode: 'production',
  entry: {
    index: __dirname + '/src/index.js',
    other: __dirname + '/src/other.js'
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  },
  optimization: {
    splitChunks: {
      //分割代码块
      cacheGroups: {//缓存组
        common: {//配置公共代码块的规则
            chunks:'initial',//从入口处就开始提取代码  后面还有异步模块
            miniSize:0,//超过大小就会被抽离
            miniChunks:2,//引用多少次会被抽离  至少引用2次就会被抽离

        },
        verder:{//第三方模块被重复引用也会被抽离单个打包
            priority:1,//优先抽离的权重,配置为1 权重越高先抽离  抽离 好单独的第三方然后再抽离 公用自定义代码 生成单独文件
            test:/node_modules/,//正则匹配,可以使用es  正则匹配返回值形式
            chunks:'initial',//从入口处就开始提取代码  后面还有异步模块,all async
            miniSize:0,//超过大小就会被抽离
            miniChunks:2,//引用多少次会被抽离  至少引用2次就会被抽离,默认为1次  2次是抽离公用代码的最小颗粒度
        }
      }
    }
  }
}
```

懒加载

```javascript
let btn = document.createElement('button')
btn.innerHTML = 'click'
btn.addEventListener('click', function() {
  import('./source.js').then(data => {
    data.dafault
  })
})
document.body.appendChild(btn)
```

如果要实现上面的懒加载功能--点击按钮异步加载一个 js 代码块 然后自己执行
实现上面功能需要 babel 的语法自动导入的插件 @babel/plugin-syntax-dynamic-import 需要再 wabpack 中设置

```javascript
//安装
npm i  @babel/plugin-syntax-dynamic-import -D
//使用
{
    test:/.js$,
    use:[
        {
            loader:'babel-loader',
            options:{
                presets:[
                    '@babel/preset-env',
                    '@babel/preset-react'
                ],
                plugins:[
                    '@babel/plugin-syntax-dynamic-import' //实现动态加载 ,在 引入的地方在defalt  上拿出来使用
                ]
            }
        }
    ]
}
// vue  react  等的路由的懒加载都是通过这种方法来实现的 实质上是jsonp
```

在实际生产中打包会生成两个 js 文件一个是正常页面使用的 index。js 还会生成一个 懒加载请求的 1.js 当执行懒加载的时候才会请求到 1.js

热更新

在开发过程中,每次保存或者代码有改变的时候会重新更新 代码重启服务器

注意:这个插件和 cleanwebpackplugin 插件同时使用会出现问题

```javascript

devServer:{
    port:3000,
    hot:true,//开启热更新
    open:true,
    contentBase:'./dist'

},
plugins:[
    new webpack.NameModulePlugin(),//告诉我们热更新的模块的名称,打印更新的模块路径
    new webpack.HotModlePrplacementPlugin()//wenpack 热更熊模块支持
]
//使用  热更新模块引入后会自动起作用  而NameModulePlugin 则要配合使用

//在js中可以写
import str from './index.js'
console.log(str)
if(module.hot){
    module.hot.accept('./index',()=>{//这个就是namemodule的作用
        console.log('文件更新了')
        let str = require('./index.js')
        console.log(str)
    })
}
```