webpack进阶

1,基本配置

2,高级配置

3,优化打包效率

4,优化产出代码

5,构建流程概述

6,babel

7,概念解读
8,热更新原理

1,commonJS只能用在服务端(标致性语言:module.exports,require,比如webpack)
2,AMD是commonJS的分支,可以用在浏览器端(标志性语言:define,require),代表库(require.js)
3,CMD是commonJS的分支,可以用在浏览器端(标志性语言:define,require),代表库(sea.js),只是比AMD优雅而已
4,UMD是commonJS+AMD结合,可以用在浏览器和服务器(标志性语言:module.exports,define,require)

浏览器能运行的文件js,html,css,png等,其中js语法由ECMA规范定义,es5的模块化需要引入AMD的require.js库,es6的模块化由es module实现。es6新增了很多新特性,所以浏览器支持es5,不支持es6。如何让浏览器解析es6?webpack

webpack有两个功能

1,相当于一个工具集,里面的babel可以使es6转为es5,loader等使less\scss文件转为css,以及vue,jsx文件等等。
2,将所有的资源打包,压缩,提升网速。

webpack5

1,package.json里webpack-dev-server命令改为:”dev":"webpack  serve --config build/webpack.dev.js
2,webpack周边插件改动:const  {merge} = require('webpack-merge')
3,const CleanWebpackPlugin = require('clean-webpack-plugin')
4,modules。rules中 loader: ['xxxx-loader']换成 use: ['xxx-loader']
5,filename: 'bundle.[contentHash:8].js'中,Hash由H换成h,即hash.

基本配置

1,基本使用

webpack是以js文件为entry入口,使用loaders处理各种模块,最后根据依赖关系进行打包的工具。webpack适合打包类似vue和react的单页面应用,不太适合打包多页面,因为单页面的js,css图片等资源互相依赖引入,而多页面之间可能没有紧密的联系。

从零搭建一个可以打包的项目流程如下:

1,webpack打包依赖nodeJS,需要下载node
2,需要初始化nodeJS模块:在空白目录 npm init生成package.json
3,在项目中包含index.html,index.js,index.css,app.js文件(vscode中安装live server插件以运行es module)。
在index.js中import 入index.css,app.js文件,这样各模块就有了依赖
4,安装webpack和webpack-cli:npm install webpack  webpack-cli --save-dev
5,安装各种loaders:npm install babel-loader babel babel-core
6,在package.json文件中:"dev": “webpack”
7,运行npm run dev即可产生打包文件dist/bundle.js
8,index.html中引入bundle.js文件

1.1,打包命令

如果只配置“dev": "webpack",默认执行  webpack.config.js里的配置内容,如果想要执行webpack.common.js或者webpack.dev.js,webpack.prod.js内容,需要:

//package.json
"scripts":{
"dev": "webpack --config webpack.dev.js",
"build": "webpack  --config webpack.prod.js",
"common": "webpack --config webpack.common.js"
}

1.2,各个loader用途 

babel-loader: 将base6编译成es5便于浏览器解析,但是目前实验发现chrome浏览器支持大部分的es6代码。

//.babelrc配置babel
{
 presets:['@babel/preset-env'],
 plugins:[]
}

css-loader和style-loader:用来解析css文件的
url-loader和file-loader: 这是webpack4里面的loader,webpack5已经弃用了!!
postcss-loader:css样式做浏览器兼容处理

//postcss.config.js
module.exports={
plugins:[require('autoprefixer')]
}

1.2.1 图片处理

之前下载了webpack5的版本,然后用url-loader和file-loader处理图片,结果css文件中的背景图总是不显示,最后才发现,webpack5内部包含了资源模块,可以使用asset/resource处理图片资源。同理,html-loader,raw-loader(以前处理txt)。所以可以选择使用webpack内部的资源模块处理资源,也可以使用url-loader处理。如果不做type属性设置,则两者同时作用,会出错。

//选择使用webpack5内部资源模块处理,type为以下内容
module.exports = {
    module:{
    rules:[
        {
             test:/\.{jpg|png|gif|jpeg)$/i,
             use: "asset/resource"
        },
        {
				test: /\.html$/,
				type: 'asset/resource',
				generator: {
					filename: 'static/[hash][ext][query]'  // 单独指定 名字
				}
		},
		{
				test: /\.svg$/,
				type: 'asset/inline'  // inline 的时候不需要指定文件名
		},
		{
				test: /\.txt$/,
				type: 'asset',
				parser: {
					dataUrlCondition: {
						maxSize: 4 * 1024 // 4kb  指定大小
					}
				}
		} 
    ]
   }
}
//使用url-loader时,type设置为'javascript/auto'
module.exports = {
   module:{
      rules:[
        {
            test:/\.jpg$/i,
            use:{
                loader:'url-loader',
                options:{
                    limit:1024,
                    outputPath:'./images'
                }
             },
            type:'javascript/auto'
        }
       ]
   }
}

1.2.2  hash的作用

每次内容有更改的时候,hash值就会改变,如果内容没更改就不会变,文件名也随着hash值改变,一旦文件名改变,就会重新访问网页,但是如果没变,网页就会有以前的缓存,就加载的更快。

1.3,plugins用途

html-webpack-plugin:将模板文件中引入生成的bundle.js,这个html作为一个新的文件放在dist中

2,配置拆分和merge

webpack打包可以拆分为开发模式,生产模式,以及公共模式。公共模式是二者都需要引用的公共部分。分别命名为webpack.common.js,webpack.dev.js,webpack.prod.js

引入webpack.common.js的方式,使用merge插件:

//webpack.dev.js或者webpack.prod.js
const smart= require('webpack-merge');
const commonConfig = require('./webpack.common.js');
module.exports = smart(commonConfig, {
   //一些配置
  mode:'development',
})

3,dev-server

在webpack.dev.js开发环境webpack配置中,一般会启用dev-server本地服务器,这个本地服务器由node.js构建,需要安装webpack-dev-server插件,启用后,构建的本地服务器使得浏览器能够检测本地代码的变化,并且自动刷新。

module.exports = smart(commonPlugin, {
  mode:'development',
  devServer: {
        contentBase: path.join(__dirname,'dist'), //静态资源服务目录
        port:8080,
        compress:true, //
        open:true, //是否刷新自动打开浏览器
        hot:true,
        host:localhost,
}
})

4,proxy代理

项目有时候会产生跨域请求,proxy代理可以将访问的接口代理到目标服务器,使得二者处于同一个域下,解决跨域请求问题。

module.exports = {
mode:'development',
proxy:{
'/api': 'http://localhost:3000/api'  //将本地api代理到localhost:3000服务器下
}
}

5,打包覆盖

默认的,下次打包的内容如果文件名相同会覆盖,文件名不同的将被保留。如果我想下次打包的时候先把以前打包的内容清空,需要在wepack中配置:CleanWebpackPlugin()
需要下载:npm install clean-webpack-plugin --save-dev

const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
plugins: [
    new CleanWebpackPlugin()
]
}

高级配置

1,默认清空上一次的dist打包结果

在plugins中使用CleanWebpackPlugin(),注意:output需要配置path,否则插件不知道清除哪个文件夹

const {CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
    output:{
        path: path.resolve(__dirname,"dist"),
    },
       plugins:[
            new CleanWebpackPlugin()
         ]
}

2,将css和js打包分离

一般js和css打包是放到一起的,但是假如css改变了,js没有改变,那么打包到一起会导致二者同时更新。webpack4.x以上使用MiniCssExtractPlugin分离css和js,它与style-loader冲突,建议放弃使用style-loader

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
    module:{
           { test: /\.css$/,
              use:[MiniCssExtractPlugin.loader, 'css-loader']
        }
    },
    plugins:[
            new  MiniCssExtractPlugin({
                  filename:"[name].css",
                  chunkFilename:"[id].css"
            })
    ]
}

3,多入口

之前将monorepo项目转为multirepo时,将各个模块拆分为npm包,最后需要将各个模块打包结果发布,之前是在每个npm包中都配置一个webpack配置文件,然后根据自己的入口出口进行打包。假如现在想 在外部只创建一个webpack.config.js文件,然后想一次性打包所有的包,需要用到多入口。

//webpack.common.js
module.exports = {
 entry:{
    one: 'src/one/index.js', //入口名one
    two: 'src/two/index.js'
},
 output:{
    path: './dist',
    filename:  '[name].bundle.js'  //[name]表示根据入口名来定义打包文件名
},
plugins:[
     new HtmlWebpackPlugin({
            template: path.join(__dirname, 'src/index.html'),  
            filename:'one.html',
            chunks: ['one' ]   //表示只引入one入口打包的结果
        }),

    new HtmlWebpackPlugin({
            template: path.join(__dirname, 'src/index.html'),
            filename:'two.html',
            chunks: ['two' ]  //表示只引入two入口打包的结果
        }),
           new  MiniCssExtractPlugin({
                  filename:"[name]/[name].css",  //将css文件放到one文件夹下
                  chunkFilename:"[id].css"
            })

]
}
//这样配置最终one文件夹下的index.js打包的bundle名为:one.bundle.js
//two文件夹下的index.js打包的bundle名为:two.bundle.js

3.1,多入口文件分离

以上的配置两个入口文件全部会被打包到一个dist文件夹下,非常的杂乱,为了生成以下简洁的目录,可以在配置的时候加上输出文件路径:
 
以下配置可以将js,html,css资源放到各自的文件夹下,但是目前我不会处理image,只能全部放到公共的文件夹images下了。

module.exports = {
  entry:{
    one: 'src/one.js',   
    two: 'src/two.js',
 },
  output:{
     filename:'[name]/[name].js'   //针对js
 },
  plugins:[
    new  MiniCssExtractPlugin({
        filename: '[name]/[name].css'  //针对css
     }),
    new HtmlWebpackPlugin({
        filename: 'one/one.html',   //针对html
        template: 'src/one/one.html'
    })
  ]
}

4,文件压缩

在development开发者模式下,文件压不压缩无所谓,但是到了线上,为了服务器访问更快,需要将打包后的js,css等资源文件压缩。压缩是在plugins里配置

5,抽离公共代码和第三方模块(分割代码)

之前是将js和css分离,现在是将第三方模块以及公共模块和最终产出的bundle.js文件分离。

第三方模块类似jquery,lodash,antd等;公共代码是同一个文件被多次引用;主要在optimization这个属性中配置。

其中one.js引用了jquery和lodash,而two.js只引用了lodash。那么有两种配置方式:

1.  将jquery和lodash打包到一起,作为一个js文件被二者的html引用。
2,将jquery和lodash分别打包,生成两个js文件,被二者的html分别引用

//将jquery和lodash打包到一起
module.exports = {
optimization:{
        splitChunks:{
            cacheGroups:{
                default:false,
                vendors:false,
                vendor:{
                    name:'vendor',
                    test:/[\\/]node_modules[\\/]/, //针对的文件夹
                    minSize:1024*1,   //生成的文件最小是多少才分割
                    chunks:'initial',  //同步,异步,all
                    minChunks:1     //被引用的最小次数
                }
            }
        }
    }
}

 针对第二种情况,直接去掉vendor:{}中的name属性即可,会自动生成各个文件的js文件,并被引用他们的chunk的html分别引用:

问题:在不做任何splitChunk配置的情况下,异步加载的模块import()会和同步加载import的模块打包到一个chunk里吗? 

回答:不会,异步加载(懒加载)会在进入这个方法的时候才打包。同步加载的模块直接打包到一起。

5.1 懒加载

以上第三方模块针对的是同步引入的静态资源, chunks:‘initial'。所谓懒加载就是异步引入某个资源,这个不是webpack独有的,是es的动态模块加载语法:

import('./data/tiles.json').then()

6,动态打包

webpack运行结果有三种方式:

scripts:{
"build": "webpack",
"watch": "webpack --watch",
"dev": "webpack-dev-server"
}

其中第一种方式表示,直接打包将结果放到dist文件夹中,每次更新代码都要手动执行打包命令。
其中第二种方式表示,直接打包将结果放到dist文件夹中,每次更新代码自动执行打包命令。
其中第三种方式表示,不生成dist文件夹,直接运行打包结果,每次更新代码自动刷新打包结果。

7,处理react(jsx)和vue文件 

 处理react(jsx)文件

1.需要下载:babel-loader,@babel/core,@babel/preset-env,@babel/preset-react 
2.需要配置.babelrc文件: {  "presets": ["@babel/preset-react"]  }
3.需要配置webpack:
      {
            test: /\.jsx$/,
            include: /src/,
            use: {
                loader: 'babel-loader',
                options:{
                        presets: "@babel/preset-env"   
                }
              }
       }                        

处理vue文件 

1.需要下载:vue-loader,应该还需要下载vue
2.需要配置webpack: 
 {
            test: /\.vue$/,
            include: /src/,
            loader: 'vue-loader',
  }  

 优化打包效率

1,babel-loader(优化从es6到es5)
2,ignorePlugin(避免引用没用的模块)
3,noParse(避免重复打包)
4,happyPack(多进程打包)
5,parallelUglifyPlugin(多进程压缩js)
6,自动刷新浏览器
7,热更新(不刷新浏览器都能更新)
8,DullPlugin(第三方库,打包以后不动了)

babel-loader

开启缓存(cacheDirectory),不会再次编译没有改变的es6代码;
明确范围(include和exclude二选一)

{
   test: /\.js$/,
   use:[ 'babel-loader?cacheDirectory'],
   include: /src/
}

 ignorePlugin

比如moment库,包含几十个语言,我们只要中英文,其他的库忽略不打包

//针对webpack配置
const webpack = require('webpack');
plugins:[
   new webpack.IgnorePlugin(  //webpack内置插件
    {
       resourceRegExp:/\.\/locale/,
       contextRegExp:/moment/
    })

]
//针对引用
import moment from 'moment';
import 'moment/locale/zh-ch'  ;//引入需要的语言插件

noParse

比如react.js被打包后生成react.min.js,那么我们项目引用react的时候,react.min.js就不要再重复打包了。 但是还是引入的哦

module:{
 noParse: [/react\.min\.js/,]
}

happyPack

多进程打包 。js是单线程,node.js也是单线程,webpack也是单线程。适用于大项目,打包时间长的效果明显,小项目使用多进程打包反而会变慢,因为启动进程以及进程之间通信都有开销。安装happypack依赖后配置:

const HappyPack = require('happypack');
module:{
    rules:[
        {
        test:/\.js$/,
        use:['happypack/loader?id=babel']
        }
    ],
},
plugins:[
    new  HappyPack({
         id:'babel',
         loaders:['babel-loader?cacheDirectory']
})
]

parallelUglifyPlugin

多个进程同时压缩js

自动刷新

之前有讲到,使用watch进行更新后自动打包生成dist,但是不会启动浏览器。这里我们将watch作为配置属性开启,然后手动打开html,这样我们改动代码,不需要重新执行打包命令,浏览器就会自动刷新。

module.exports = {
  watch:true
}

不过如果配置了webpack-dev-server,那这个属性就没必要配置了。 

 热更新

网页不刷新,新代码生效,状态不丢失。热更新需要配合webpack-dev-server使用。
首先下载依赖:webpack-dev-server。其中如果webpack版本是5,运行该依赖的webpack-cli不能是4,可以下载版本3(比如:3.3.12)

plugins:[
   new webpack.HotModuleReplacementPlugin()
],
devServer:{
  port:5500,
  open:true,
  hot: true  //配置了HMR后,将hot设置为true即可实现热更新
}

babel

1,babel-polyfill,@babel/runtime,@babel/plugin-transform-runtime

详细阅读

概念解读

1,module,chunk,bundle概念

module(模块):所有使用import导入的都是模块,包括(js,css,png,jsx,json,vue)等等资源,但是作为模板的html不是模块

chunk:多个模块合并成的,可以理解为多个模块经过webpack打包生成的(js,css)等文件在内存中的内容。entry,import(),splitChunk可以定义chunk。

bundle:chunk还在内存中,bundle是实实在在打包的结果。一个chunk对应一个bundle。

2,webpack打包流程

1,生成options对象(合并webpack.config.js中所有参数)
2,实例化compiler对象(将options传递进去)
3,实例化compilition对象(compiler.run函数编译生成该对象)
4,分析入口文件,调用AST引擎生成AST语法树,遍历所有的依赖
5,调用loader,将各种类型模块生成js模块,同时生成AST语法树,继续遍历依赖
6,对生成所有的module,调用plugins,合并,拆分,生成chunk
7,将chunk生成对应的bundle输出

3,热更新原理

参考链接

准备工作:
1,本地下载webpack-dev-server,wds通过express启动服务端
2,本地项目已经处于服务器中
3,配置wds相关options,使得打包后的bundle与浏览器能够建立websocket通讯
开始监听:
1,本地服务端代码发生改变
2,webpack监听到变化,重新编译生成新hash值,hot-update.json,hot-update.js
3,通过socket将hash发送到客户端,
4,客户端对比hash变化,如果一致直接到缓存拿,不一致通过ajax获取hot-update.json
5,hot-update.json中包含hash值,此时再通过jsonp获取hot-update.js

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值