APP下载
报价宝  ›  科技  › 

webpack总结篇

报价宝 来源:baojiabao.com 发布时间:2019-10-31 04:56:00 11月14日更新
报价宝综合消息webpack总结篇

一、webpack简介

1.1 版本更迭

image.png

大版本变化

image.png

1.2 功能进化

Webpack V1

编译、打包HMR (模组热更新)程式码分割档案处理Webpack V2

Tree ShakingES module动态 Import新的文件Webpack V3

Scope Hoisting (作用域提升)Magic Comments (配合动态import使用)版本迁移

V1 -> V2

迁移指南 https://doc.webpack-china.org/guides/migrating/

V2 -> V3

更新升级即可

二、webpack核心概念

2.1 Entry

程式码的入口打包的入口单个或多个写法建议使用键值对写法

module.exports = {

entry: 'index.js'

}

module.exports = {

entry: '[index.js','vendor.js']

}

module.exports = {

entry: {

index:'index.js'

}

}

module.exports = {

entry: {

index:['index.js','app.js'],

vendor: 'vendor.js'

}

}

2.2 Output

打包成的档案(bundle)一个或多个自定义规则配合CDNmodule.exports = {

entry: 'index.js',

output: {

filename: 'index.min.js'

}

}

module.exports = {

entry: {

index: 'index.js',

vendor: 'vendor.js'

},

output: {

filename: '[name].min[hash:5].js'

}

}

2.3 Loaders

处理档案转化为模组module.exports = {

module: {

rules: [

{

test: /.css$/,

use: 'css-loader'

}

]

}

}

2.3.1 常用Loader

编译相关

babel-loaderts-loader样式相关

style-loadercss-loaderless-loaderpostcss-loader档案相关

file-loaderurl-loader2.4 Plugins

参与打包整个过程打包优化和压缩配置编译时的变数极其灵活module.exports = {

plugins: [

new webpack.optimize.UglifyJsPlugin()

]

}

2.4.1 常用plugins

优化相关

CommonsChunkPluginUglifyJsWebpackPlugin功能相关

ExtractTextWebpackPlugin 提取cssHtmlWebpackPlugin 生成HTML模板HotModuleReplacementPlugin 热模组替换CopyWebpackPlugin 拷贝档案2.5 名词

Chunk 打包过程分割的程式码块Bundle 打包后的档案Module三、初探 webpack

3.1 使用babel打包es6

3.1.1 编译 ES 6/7

Babel

npm install [email protected] @babel/core

npm install –save-dev babel-loader babel-core

Babel Presets

主要有几种型别选择

es2015es2016es2017envbabel-preset-reactbabel-preset-stage 0 - 3npm install @babel/preset-env –save-dev

npm install babel-preset-env –save-dev

Babel Polyfill

针对一些不能处理的函式方法(Generator、Set、Map、Array.from...)需要用到babel-Polyfill处理

全域性垫片为应用准备npm install babel-polyfill –save

import “babel-polyfill”

Babel Runtime Transform

区域性垫片为开发框架准备npm install babel-plugin-transform-runtime –save-dev

npm install babel-runtime –save

例子

module.exports = {

entry: {

app: 'app.js'

},

output: {

filename: '[name].[hash:8].js'

},

module: {

rules:[

test: /.js$/,

use: {

loader: 'babel-loader',

options: {

presets: [

'@babel/preset-env',{

//指定target为根据哪些语法编译

targets: {

browsers: ['> 1%','last 2 versions']

}

}

]

}

},

exclude: '/node_modules'

]

}

}

对于webpack中babel的配置可以单独提取处理.babelrc统一管理

{

"presets": [

["@babel/preset-env",{

targets: {

browsers: ['> 1%','last 2 versions']

}

}]

],

"plugins": [

"transform-runtime"

]

]

3.2 打包 Typescript

npm i typescipt ts-loader --save-dev

npm i typescipt awesome-typescript-loader --save-dev

配置

tsconfig.jsonwebpack.config.jstsconfig

配置选项:官网/docs/handbook/compiler-options.html常用选项 compilerOptions include exclude宣告档案

用于编译时检查错误

以loadsh为例,需要安装@types/lodash带有宣告档案的,而不是安装lodash

npm install @types/lodash

npm install @types/vue

Typings

也可以这样安装带有type的包

npm install typings

typings install lodash

例子

module.exports = {

entry: {

'app': 'app.js'

},

output: {

filename: '[name].bundle.js'

},

module: {

rules: [

test: /.tsx?$/,

use: {

loader: 'ts-loader'

}

]

}

}

在专案根目录建立tsconfig.json

{

"compilerOptions": {

"module": "comonjs",

"target": "es5", //编译后的档案在哪个环境执行

"allowJs": true,//允许js语法

},

"include": [

//编译路径

"./src/*"

],

"exclude": [

//排除编译档案

"./node_modules"

]

}

3.3 提取 js 的公用程式码

减少程式码冗余提高载入速度主要使用内建外挂实现webpack.optimize.CommonsChunkPlugin

{

plugins: [

new webpack.optimize.CommonsChunkPlugin(option)

]

}

例子

module.exports = {

entry: {

"pageA": "./src/pageA",

"pageB": "./src/pageB",

"vendor": ['loash']//业务程式码和第三方程式码区分开,给loash单独打一个包

},

output: {

path: path.resolve(__dirname, './dist'),

filename: '[name].bundle.js',

chunkFilename: '[name].chunk.js'

},

plugins: [

// 提取common

new webpack.optimize.CommonsChunkPlugin({

name: 'common',

minChunks:2,//出现两次就打包成common程式码

chunks: ['pageA','pageB']//指定范围提取公共程式码

})

// 提取vendor、取业务程式码manifest

new webpack.optimize.CommonsChunkPlugin({

//把entry的vendor程式码和这里的common(webpack打包的)程式码合并

names: ['vendor','manifest']

minChunks: Infinity

})

// ==== 提取业务程式码manifest== 合并到上面names中

// 如果不想把webpack打包的程式码和vendor程式码合并 需要提取到manifest

//new webpack.optimize.CommonsChunkPlugin({

// webpack程式码和vendordiam区分开

// name: 'manifest' //manifest即生成

// minChunks: Infinity

// })

]

}

image.png

3.4 程式码分割和懒载入

第一种方式:通过wepack内建方法

require.ensure动态载入一个模组,接收四个引数

[]:dependencies 初次并不会执行callback的时候才会执行errorCallback 可省略chunkName第二种方式:通过ES2015 Loader Spec

System.import()后面演变为import()来动态载入模组

import()方式返回一个promise在import中传入需要依赖的明,动态载入模组,就可以像使用Promise一样使用import().then()

程式码分割场景

分离业务程式码 和 第三方依赖分离业务程式码 和 业务公共程式码 和 第三方依赖分离首次载入 和 访问后加载的程式码例子

module.exports = {

entry: {

"pageA": "./src/pageA"

},

output: {

path: path.resolve(__dirname, './dist'),

publicPath: './dist/',//动态载入路径

filename: '[name].bundle.js',

chunkFilename: '[name].chunk.js'

}

}

目标是提取pageA、pageB中公共的模组moduleA

//src/pageA.js

import './subPageA'

import './subPageB'

//ensure的时候程式码不会执行 需要在下面载入一次

require.ensure(['lodash'],function(){

var _ = require('lodash')

_.join([1,2,3])

},'vendor')// 指定chunk名称

export default 'pageA'

执行打包这时loadash提取到vendor中

image.png

这时候还不是我们想要的

//src/pageA.js

require.include('./moduleA')

if(page === 'subPageA'){

require.ensure(['./subPageA'],function(){

var subPageA = require('./subPageA')

},'subPageA')// 指定chunk名称

}else{

require.ensure(['./subPageB'],function(){

var subPageB = require('./subPageB')

},'subPageB')// 指定chunk名称

}

//ensure的时候程式码不会执行 需要在下面载入一次

require.ensure(['lodash'],function(){

var _ = require('lodash')

_.join([1,2,3])

},'vendor')// 指定chunk名称

export default 'pageA'

image.png

这时候这有pageA中才有moduleA

image.png

新建一个html验证是否动态载入

image.png

import()动态载入的写法

//src/pageA.js

require.include('./moduleA')

var page = 'subPageA'

if(page === 'subPageA'){

// 指定chunkName /** webpackChunkName: 'subPageA' **/

import(/** webpackChunkName: 'subPageA' **/,'./subPageA').then(function(subPageA){

console.log(subPageA)

})

}else{

import(/** webpackChunkName: 'subPageB' **/'./subPageB').then(function(subPageB){

console.log(subPageB)

})

}

// 异步载入lodash

//ensure的时候程式码不会执行 需要在下面载入一次

require.ensure(['lodash'],function(){

var _ = require('lodash')

_.join([1,2,3])

},'vendor')// 指定chunk名称

export default 'pageA'

如果/** webpackChunkName: 'subPageA' **/相同,则会合并处理

合并了subPageA和subPageB

image.png

来看看打包后的档案,既有A、B

image.png

在webpack程式码分割中使用async异步载入

module.exports = {

entry: {

"pageA": "./src/pageA",

"pageB": "./src/pageB",

"vendor": ['loash']//业务程式码和第三方程式码区分开,给loash单独打一个包

},

output: {

path: path.resolve(__dirname, './dist'),

filename: '[name].bundle.js',

chunkFilename: '[name].chunk.js'

},

plugins: [

// add 异步模组

new webpack.optimize.CommonsChunkPlugin({

aysnc:'async-common',//异步共同的东西

children: true,

names: ['vendor','manifest']

minChunks: Infinity

})

// 提取vendor、取业务程式码manifest

new webpack.optimize.CommonsChunkPlugin({

//把entry的vendor程式码和这里的common(webpack打包的)程式码合并

names: ['vendor','manifest']

minChunks: Infinity

})

]

}

//src/subPageA.js

import './moduleA'

console.log('this. is subPageA')

export default 'subPageA'

//src/subPageB.js

import './moduleA'

console.log('this. is subPageB')

export default 'subPageB'

//src/subPageB.js

import './moduleA'

console.log('this. is moduleA')

export default 'moduleA'

//src/pageA.js

// 异步载入不能include 否则会和pageA打包到一起

// require.include('./moduleA')

import _ from 'lodash'

var page = 'subPageA'

if(page === 'subPageA'){

// 指定chunkName /** webpackChunkName: 'subPageA' **/

import(/** webpackChunkName: 'subPageA' **/,'./subPageA').then(function(subPageA){

console.log(subPageA)

})

}else{

import(/** webpackChunkName: 'subPageB' **/'./subPageB').then(function(subPageB){

console.log(subPageB)

})

}

// === lodash不在异步载入

//ensure的时候程式码不会执行 需要在下面载入一次

//require.ensure(['lodash'],function(){

// var _ = require('lodash')

// _.join([1,2,3])

//},'vendor')// 指定chunk名称

export default 'pageA'

//src/pageB.js

import _ from 'lodash'

var page = 'subPageB'

if(page === 'subPageA'){

// 指定chunkName /** webpackChunkName: 'subPageA' **/

import(/** webpackChunkName: 'subPageA' **/,'./subPageA').then(function(subPageA){

console.log(subPageA)

})

}else{

import(/** webpackChunkName: 'subPageB' **/'./subPageB').then(function(subPageB){

console.log(subPageB)

})

}

// === lodash不在异步载入

//ensure的时候程式码不会执行 需要在下面载入一次

//require.ensure(['lodash'],function(){

// var _ = require('lodash')

// _.join([1,2,3])

//},'vendor')// 指定chunk名称

export default 'pageB'

image.png

image.png

这样就把subpageA和subPageB共同依赖的moduleA异步提取出来

3.5 处理 CSS 和 CSS 模组化

引入css

需要两个loader,style-loader(建立标签到文件流中)、css-loader(可以import一个样式档案,使得在js中可以使用)

Style-Loader

style-loader除了本身,还有这几个loader

style-loader/url 可以注入link标签到页面style-loader/useable 控制样式是否插入到页面中Style-Loader的options

insertAt (插入位置)insertInto (插入到dom)singleton (是否只使用一个style 标签)transform (转化,浏览器环境下,插入页面前)例子

module.exports = {

entry: {

app: './src/app.js'

},

output: {

path: path.resolve(__dirname, 'dist'),

publicPath: './dist/', //指定从专案中哪里引入资源

filename: '[name].bundle.js'

},

module: {

// loader解析从后往前处理

rules: [

{

test: /.css$/,

use: [

{

//loader: 'style-loader',

loader: 'style-loader/url', //使用这个可以往页面注入link标签 而不是style,这个并不常用

},

{

//loader: 'css-loader',

loader: 'file-loader'//使用这个可以往页面注入link标签 而不是style 这个并不常用

}

]

}

]

}

}

CSS-Loader

options

alias (解析的别名)importLoader (@import )Minimize (是否压缩)modules (启用css-modules)CSS-Modules

localIdentName: '[path][name]__[local]--[hash:base64:5]'

例子

module.exports = {

entry: {

app: './src/app.js'

},

output: {

path: path.resolve(__dirname, 'dist'),

publicPath: './dist/', //指定从专案中哪里引入资源

filename: '[name].bundle.js'

},

module: {

// loader解析从后往前处理

rules: [

{

test: /.css$/,

use: [

{

loader: 'style-loader',

options: {

//合并多个style为一个

singleton:true

}

},

{

loader: 'css-loader',

options: {

minimize:true,

modules: true,

// css模组化

localIdentName: '[path][name]_[local]_[hash:base64:5]'

}

}

]

}

]

}

}

配置Less / Sass

npm install less-loader less --save-dev

npm install sass-loader node-sass --save-dev

例子

module.exports = {

entry: {

app: './src/app.js'

},

output: {

path: path.resolve(__dirname, 'dist'),

publicPath: './dist/', //指定从专案中哪里引入资源

filename: '[name].bundle.js'

},

module: {

// loader解析从后往前处理

rules: [

{

test: /.(css|less)$/,

use: [

{

loader: 'style-loader',

options: {

//合并多个style为一个

singleton:true

}

},

{

loader: 'css-loader',

options: {

minimize:true,

modules: true,

// css模组化

localIdentName: '[path][name]_[local]_[hash:base64:5]'

}

},

{

loader: 'less-loader'

},

{

loader: 'sass-loader'

}

]

}

]

}

}

提取 CSS

extract-loaderExtractTextWebpackPlugin例子

module.exports = {

entry: {

app: './src/app.js'

},

output: {

path: path.resolve(__dirname, 'dist'),

publicPath: './dist/', //指定从专案中哪里引入资源

filename: '[name].bundle.js'

},

module: {

// loader解析从后往前处理

rules: [

{

test: /.(css|less)$/,

use:

ExtractTextWebpackPlugin.extra({

// 提取css并不会自动加入到文件中,需要在HTML手动加入css档案

fallback: {

loader: 'style-loader',

options: {

//合并多个style为一个

singleton:true

}

},

// 处理css

use: [

{

loader: 'css-loader',

options: {

minimize:true,

modules: true,

// css模组化

localIdentName: '[path][name]_[local]_[hash:base64:5]'

}

},

{

loader: 'less-loader'

},

{

loader: 'sass-loader'

}

]

})

}

]

},

plugins: [

new ExtractTextWebpackPlugin({

filename: '[name].min.css',

allChunks: false //指定提取css范围,true所有import进来的css

})

]

}

image.png

3.6 PostCSS in Webpack

安装

postcsspostcss-loaderAutoprefixercssnanopostcss-cssnext例子

module.exports = {

entry: {

app: './src/app.js'

},

output: {

path: path.resolve(__dirname, 'dist'),

publicPath: './dist/', //指定从专案中哪里引入资源

filename: '[name].bundle.js'

},

module: {

// loader解析从后往前处理

rules: [

{

test: /.(css|less)$/,

use:

ExtractTextWebpackPlugin.extra({

// 提取css并不会自动加入到文件中,需要在HTML手动加入css档案

fallback: {

loader: 'style-loader',

options: {

//合并多个style为一个

singleton:true

}

},

// 处理css

use: [

{

loader: 'css-loader',

options: {

minimize:true,

modules: true,

// css模组化

localIdentName: '[path][name]_[local]_[hash:base64:5]'

}

},

{

loader: 'less-loader'

},

{

loader: 'sass-loader'

},

{

loader: 'postcss-loader',

options: {

ident: 'postcss',

plugins: [

require('antoprefixer')()

]

}

}

]

})

}

]

},

plugins: [

new ExtractTextWebpackPlugin({

filename: '[name].min.css',

allChunks: false //指定提取css范围,true所有import进来的css

})

]

}

3.7 Tree shaking

3.7.1 JS Tree shaking

使用场景

常规优化引入第三方库的某一个功能例子

module.exports = {

entry: {

app: './src/app.js'

},

output: {

path: path.resolve(__dirname, 'dist'),

publicPath: './dist/', //指定从专案中哪里引入资源

filename: '[name].bundle.js'

},

module: {

rules: [

{

test: /.js$/,

use: [

{

loader: 'babel-loader',

options: {

presets: ['env'],

plugins: [

// lodash Tree shaking

'lodash'

]

}

}

]

}

]

},

plugins: [

new ExtractTextWebpackPlugin({

filename: '[name].min.css',

allChunks: false //指定提取css范围,true所有import进来的css

})

// Tree shaking

new webpack.optimize.UglifyJsPlugin({

})

]

}

有些库不是es模组写的,并不能tree shaking。需要借助其他工具

npm i babel-loader babel-core babel-preset-env babel-plugin-lodash --save

lodash Tree不生效

lodash-es --> nobabel-lugin-lodash --->working检视模组是否Tree Shaking方式:去第三方库index.js中看模组书写方式是否是es

3.7.2 CSS Tree shaking

主要使用 purifycss-webpack

例子

const glob = require('glob-all')

module.exports = {

entry: {

app: './src/app.js'

},

output: {

path: path.resolve(__dirname, 'dist'),

publicPath: './dist/', //指定从专案中哪里引入资源

filename: '[name].bundle.js'

},

module: {

rules: [

{

test: /.js$/,

use: [

{

loader: 'babel-loader',

options: {

presets: ['env'],

plugins: [

// lodash Tree shaking

'lodash'

]

}

}

]

}

]

},

plugins: [

new ExtractTextWebpackPlugin({

filename: '[name].min.css',

allChunks: false //指定提取css范围,true所有import进来的css

})

// 放到ExtractTextWebpackPlugin后面

new PurifyCss({

paths: glob.sync([

path.join(__dirname,'./*.html'),

path.join(__dirnname,'./src/*.js')

])

})

// Tree shaking

new webpack.optimize.UglifyJsPlugin({

})

]

}

四、由浅入深Webpack

4.1 档案处理

4.1.1 图片处理

css中引入图片自动合成雪碧图压缩图片Base64编码处理需要用到的loader

file-loader css中引入图片url-loader base64编码img-loader 压缩图片postcss-sprites合成雪碧图4.1.2 处理雪碧图、base64、压缩图片

module.exports = {

module: {

rules: [

{

test: /.(css|less)$/,

use:

extractLess.extract({

// 提取css并不会自动加入到文件中,需要在HTML手动加入css档案

fallback: {

loader: 'style-loader',

options: {

//合并多个style为一个

singleton:true

}

},

// 处理css

use: [

{

loader: 'css-loader',

options: {

minimize:true,

modules: true,

// css模组化

localIdentName: '[path][name]_[local]_[hash:base64:5]'

}

},

{

loader: 'less-loader'

},

{

loader: 'sass-loader'

},

{

loader: 'postcss-loader',

options: {

ident: 'postcss',

plugins: [

// 合并雪碧图

require('postcss-sprites')({

// 指定雪碧图输出路径

spritePath: 'dist/assets/imgs/sprites',

retina: true // 处理苹果高清retina 图片命名需要 [email protected],对应的图片的css大小设定也要减小一半

})

]

}

}

]

})

},

{

test: /.(png|jpg|gif)$/,

use:[

//{

// loader: //'file-loader',//处理图片

// options: {

// publicPath:'',// 使得图片地址可以访问

// outputPath: 'dist/'

// useRelativePath:true

//}

//}

// url-loader会把图片转成base64

{

loader: 'url-loader',

options: {

name: '[name].min.[ext]' //5kb内会转成base64 ,否则输出图片路径

limit: 1000,

publicPath:'',

outputPath: 'dist/',

useRelativePath:true

}

},

// 压缩图片

{

loader: 'img-loader',

options: {

pngquant: {

//图片质量

quality:80

}

}

},

]

}

]

}

}

4.1.2 处理字型档案

file-loaderurl-loadermodule.exports = {

module: {

rules: [

{

test: /.(css|less)$/,

use:

extractLess.extract({

// 提取css并不会自动加入到文件中,需要在HTML手动加入css档案

fallback: {

loader: 'style-loader',

options: {

//合并多个style为一个

singleton:true

}

},

// 处理css

use: [

{

loader: 'css-loader',

options: {

minimize:true,

modules: true,

// css模组化

localIdentName: '[path][name]_[local]_[hash:base64:5]'

}

},

{

loader: 'less-loader'

},

{

loader: 'sass-loader'

},

{

loader: 'postcss-loader',

options: {

ident: 'postcss',

plugins: [

// 合并雪碧图

require('postcss-sprites')({

// 指定雪碧图输出路径

spritePath: 'dist/assets/imgs/sprites',

retina: true // 处理苹果高清retina 图片命名需要 [email protected],对应的图片的css大小设定也要减小一半

})

]

}

}

]

})

},

{

test: /.(png|jpg|gif)$/,

use:[

//{

// loader: //'file-loader',//处理图片

// options: {

// publicPath:'',// 使得图片地址可以访问

// outputPath: 'dist/'

// useRelativePath:true

//}

//}

// url-loader会把图片转成base64

{

loader: 'url-loader',

options: {

name: '[name].min.[ext]' //5kb内会转成base64 ,否则输出图片路径

limit: 1000,

publicPath:'',

outputPath: 'dist/',

useRelativePath:true

}

},

// 压缩图片

{

loader: 'img-loader',

options: {

pngquant: {

//图片质量

quality:80

}

}

},

// 处理字型档案

{

test: /.(eot|woff2|woff|ttf|svg)$/,

use: [

{

loader:'url-loader',

options: {

name: '[name].min.[ext]' //5kb内会转成base64 ,否则输出图片路径

limit: 5000,

publicPath:'',

outputPath: 'dist/',

useRelativePath:true

}

}

]

}

]

}

]

}

}

4.1.3 处理第三方 JS 库

处理第三方库 用到providePlugin、imports-loader

1.providePlugin

以引入jQuery为例

npm i jquery

module.exports = {

plugins: [

new webpack.ProvidePlugin({

// 在全域性注入jQuery变数

$:'jquery'

})

]

}

引入本地libs中的jQuery

module.exports = {

resolve: {

alias: {

// $确切匹配 把jQuery这个关键字解析到某个目录下

jquery$:path.resolve(__dirname,'src/libs/jquery.min.js')

}

},

plugins: [

new webpack.ProvidePlugin({

// 在全域性注入jQuery变数

$:'jquery'

})

]

}

2.imports-loader

module.exports = {

module:{

rules:[

{

test:path.resolve(__dirname,'src/app.js'),

use:[

{

loader: 'imports-loader',

options: {

$: 'jquery'

}

}

]

}

]

}

}

4.2 HTML in webpack(自动生成HTML)

自动生成HTML,把这个页面需要的js、css插入到页面中

4.2.1 生成 HTML

htmlWebpackPlugin

options

templatefilenameminify 是否压缩chunksinject 是否让css、js通过标签形式插入到页面中module.exports = {

entry: {

app: './src/app.js'

},

output: {

path: path.resolve(__dirname, 'dist'),

filename: '[name].bundle.js'

},

plugin:[

new htmlWebpackPlugin({

filename:'index.html', // 不传预设index.html

template: './index.html',//传入模板

inject:true,//控制jscss是否插入到页面

minify: {

collapseWhitespace:true //压缩空格

},

chunks:['app']//指定chunks会把跟这个入口相关的chunks插入到页面中

})

]

}

4.2.2 HTML 中引入图片

需要用到html-loader

文章标签: 报价宝 降噪耳机价格 耳机价格 红米手机价格 华为手机价格 小米手机价格 电视机价格 笔记本电脑价格 笔记本价格 汽车价格 汽车价格 笔记本电脑价格 小米手机价格 华为手机价格 红米手机价格