重学webpack

一:基本配置

1. webpack默认的配置只能解析js,和json 

webpack启动配置 内置了很多的函数功能,目的是为了让浏览器能解析我们js(因为还有浏览器不支持require ecports module)

因为为了解决浏览器支持而内置的js功能,导致webpack不适合构建库;适合构建项目工程,用强大的编译功能提高效率

vue、react源码是用rollup 代码比较纯粹。

const path =  require('path')
module.exports = {
entry:'./src/index.js',
mode:'development',
// mode:'production', //会代码分割,会压缩,output后的文件是1.main.js
output:{
path:path.resolve(__dirname,'./dist'), //path:绝对路径的字符串
filename:'main.js'
}
}

2.webpack有默认的配置文件,叫webpack.config.js 

"script":{
"dev":"webpack",  //使用默认的配置文件
"build":"webpack --config webpack-config.js" // 或者通过--config webpack-config.js 来指定webpack使用哪个配置文件来执行构建
}

3.entry (入口) 可以是字符串、数组、对象

module.exports = {
 entry:'./src/index.js' //可以是字符串
 // 数组 entry:['./src/index.js']  // 多个数组元素会被打包到一个文件
// 对象 entry:{
  // index:'./src/index.js'
   // }

}

多入口多出口 如果多入口一个出口就会出错

entry:{
main:'./src/index.js',
other:'./src/other.js'
},
output:{
path:path.resolve(__dirname,"./dist"),
filename:"[name].js"  //name是占位符
} 

4. mode:none、development、production

默认设置的是 production

开发阶段的开启有利于热更新的处理,识别哪个模块的变化;

生产阶段的开启有利于模块压缩、处理副作用等一些功能

5.output -> filename:[name]_[chunkhash:8].js

hash chunkhash contenthash

hash 整个文件hash值都相同 一处改变 其他都会变

chunkhash 对应的css和js文件改变 与其关联的hash值就会改变 但其内容并未改变 这样就达不到缓存的效果

contenthash(针对内容,只要内容不一样,hash值就会不一样)

js 推荐使用chunkhash

在使用chunkhash时候 css改变后 引入css的js打包后文件也会改变。所以contenthash就出来了,针对内容改变才会去改变 

css 推荐contenthash 把css都抽离出对应的css文件加以引用

6.loader

module:{
  rules:[
    {
      test:/.css$/,
    //loader的执行顺序 从右往左
  // css-loader: 把样式代码写入js ,这一步还不够,样式不起作用
  // style-loader:把写入的js样式,插入到index.html中 (html.head.style中)
      use:["style-loader","css-loader"]
   },
   {
     test:/.png$/,
     //use:["file-loader"] 
     //给loader添加参数
    use:{
      loader:'file-loader',
      options:{
      name:"[name].[ext]
      }
     }
    }
  ]
} 

7.plugins

html-webpack-plugin

//会在打包结束后,自动生成一个html文件,并把打包生成的js模块引入到html中
const htmlWebpackPlugin = require("html-webpack-plugin-)
plugins:[
  new htmlWebpackPlugin({
    title:'测试标题',
    template:'./index.html' ,//使用哪一个作为模板
    filename:'index.html', //打包后的filename
  })
  
// 设置完标题后需要在index.html中 将title更换掉
//<title> <%= htmlWebpackPlugin.options.title %></title>
]    

clean-webpack-plugin

plugins:[
//在构建之前,将dist文件删除掉
new CleanWebpackPlugin()
]

mini-css-extract-plugin

//把css提取成独立文件
{
 test:/.css$/,
use:[MiniCssExtractPlugin.loader,"css-loader"]
}

new MiniCssExtractPlugin({
filename:"[name][chunkhash:8].css"
})

8.sourceMap 

// none 若要关闭配置,则设置为none
// source-map:产生.map文件、
// eval:速度最快,使用eval包裹模块代码、
// cheap:较快,不包含列信息、
// Module:第三方模块,包含loader的sourcemap(比如jsx to js ,babel的sourcemap)、
// inline:将.map作为dataURI嵌入,不单独生产.map文件

配置推荐:
devtool:"cheap-module-eval-source-map" //开发环境部署
//线上不推荐开启devtool:暴露代码&影响解析速度
devtool:'cheap-module-source-map'  //线上生成配置

二.热更新&热模块更新

热更新 每次修改后浏览器会自动刷新一次

热模块更新 修改css 时候 局部更新

webpack插件中自带了HotModuleReplacement (HMR)

plugins:[
webpack.HotModuleReplacementPlugin()
]

可以在devServer.hot 设置为true 就会开启热更新

hotOnly:true 无论代码如何改变 都不会刷新浏览器

但是当手动更改原生js时候 需要手动的去监听

 if(module.hot){
 module.hot.accept('./index.js',()=>{
 //重新执行一遍操作
})
}

用到vue、react、angular时候 webpack提供了相应的loader 例如vue-loader 只有原生js需要手动去监听变化然后热更新

module:{
  rules:[
   {
    test:/.vue$/,
   loader:['vue-loader']
    }
]
}
css-loader:把css代码打包到js中 解析css 一般是 @import和url()两部分(样式不生效)
style-loader:把写入的js代码插入到index.html的head中  (样式生效)
vue-style-loader 同style-loader是把打包后的js代码中的css代码插入到index.html 他还可以处理.vue文件中的 <style> 模块中的样式代码 是没有热更新的 在ssr时候做了一些操作
vue-loader 是针对vue框架采用的 有热更新
 
三.babel
需要安装
//需要安装
npm i babel-loader @babel/core @babel/preset-env -D

1. babel-loader 是webpack与babel的通信桥梁,不会做 把es6转成es5的工作,这部分工作需要用到@babel/preset-env来做

2. @babel/core提供了核心的库和API。 

3. @babel/preset-env 里包含了es6、7、8转es5的转换规则

{
   test:/.js$/,
   use:{
   loader:'babel-loader',
    options:{
        presets:["@babel/preset-env']
    }
   }
}

只转换了const let 箭头函数等一些基础的语法

但是promise等新特性 低版本浏览器不知道

方法:把新特性的语法引进来 polyfill (包含了所有新特性)

//需要安装 @babel/polyfill
nom i @babel/polyfill -S

打包后的体积变大了,因为polyfill把所有新特性都引进来了,我们需要实现按需加载

{
   test:/.js$/,
   use:{
   loader:'babel-loader',
    options:{
        presets:["@babel/preset-env',{
       targets:{
       edge:"17",
       firefox:"60",
       chrome:"67",
       safari:"11.1"
    },
    corejs:2, // 新版本需要指定核心库版本
    useBuiltIns:"usage" //按需注入 ,有3个可选值,推荐使用usage
   }]
    }
   }
}

polyfill与transform-runtime的区别 

polyfill是以什么方式引入新特性:直接挂在全局对象上 window 造成的后果:会污染全局变量

polyfill 不适合构建开源库 UI库

当我们开发组件库,工具库的时候 polyfill就不适合了,因为polyfill是注入到全局变量,window下会污染全局环境,所以推荐闭包方式:@babel/plugin-transform-runtime 它不会造成全局污染

transform-runtime

//需要安装
npm i @babel/plugin-transform-runtime -D
npm i @babel/runtime -S
{
  test:/.js$/,
  use:{
 loader:'babel-loader',
 "plugins":[
 [
    "@babel/plugin-transform-runtime",
 {
   "absoluteRuntime":false,
   "corejs":false,
    "helpers":true,
    "regenreator":true,
   "useESModules":false
 }]
]
}
}

 可以把上述代码抽出来,放在.babelrc文件中

 {  
"presets":[
  ["@babel/preset-env',{
       targets:{
       edge:"17",
       firefox:"60",
       chrome:"67",
       safari:"11.1"
    },
    corejs:2, // 新版本需要指定核心库版本
    useBuiltIns:"usage" //按需注入 ,有3个可选值,推荐使用usage
   }]
],
//plugins 当react代码不是用jsx的语法 而是 createElement的方式 ,需要将react转化一下
//vue 就用 transform-vue-jsx
"plugins":["@babel/presets-react"]
}

 三.webpack 性能优化(能用绝对路径的就用绝对 绝对查询速度高于相对)

1.loader 

 include & exclude

2. 

resolve:{
//用来配置webpack去哪里找第三方相关的模块
modules:[path.resolve(__dirname,"./node_modules")],
//设置别名 来将原导入路径映射成一个新的导入路径
//默认情况下,webpack会从入口文件 ./node_modules/bin/react/index开始递归解析和处理依赖文件,我们可以直接指定文件,避免此处的耗时
alias:{
 "@":path.join(__dirname,"src"),
"react":path.resolve(__dirname,"./node_modules/react-dom/umd/react.production.min.js"),
"react-dom"...
},
//在导入语句没带后缀时,webpack会自动带上后缀后,去查找文件是否存在
// 后缀尝试列表尽量的少,导入语句尽量带上后缀
extensions:[".js",".json"]
}

 3.tree shaking

①.css 

npm i glob-all purify-css purifycss-webpack --save-dev

const PurifyCSS = require('purifycss-webpack')
const glob = require('glob-all')

plugins:[

new PurifyCSS({
 path:glob.sync([
  //要做css tree shaking的路径文件
   path.resolve(__dirname,'./src/*.html')  //请注意,我们同样需要对html文件进行tree shaking
    path.resolve(__dirname,'./src/*.js')
  ])
})
]

②.js(只支持es module 的模块方式 import方式引入,不支持commonjs  require的方式引入)

js tree shaking在开发模式下 是不生效的 只有在生产环境才会生效 为了方便调试

optimization:{
   usedExports:true //哪些导出的模板被使用了
}

只要将mode设置为production 就会自动开启js 摇树功能

 4. V8带来的优化

①:for...of替代forEach

②:Map和Set替代Array和Object

③:includes替代indexOf

 
原文地址:https://www.cnblogs.com/alhh/p/13071545.html