webpack4学习笔记分享

webpack 4

webpack到底是什么


wepack is a module bundle:模块打包工具。javascript

模块:不限于js文件,CSS文件,图片等等。css

ES Module CommonJs AMD CMDhtml

安装方式:java

  1. 全局 npm install webpack webpack-cli -gnode

  2. 项目中安装 (推荐)react

    1. npm install webpack webpack-cli --save-devwebpack

    2. npm install webpack webpack-cli -Dgit

      解决npx webpack -ves6

      npx命令会去当前项目目录的node-modules寻找webpackgithub

    3. 初始化 npm init -y(自动生成默认配置项)

    4. npm info webpack(查看包的版本号)

    5. npm install 下载全部依赖包

注:不建议安装在全局。不一样两个项目,若是webpack版本号不同,一个3,一个4,其中一个运行不起来

使用webpack的配置文件


  • 没有配置文件时会有默认的配置,但必须指定文件名:npx webpack index.js
  • webpack.config.js(默认) 可使用 npx webpack --config mywebpackconfig.js
const path = require('path')
module.exports = {
    mode: 'production(压缩代码)/development(不压缩代码)' //默认production
    entry: {
    	main: './src/index.js'
	},
    output: {
        filename: '[name].js',   //占位符
        path: path.resolve(__dirname, 'bundle'),  //绝对路径
        publicPath: "http://baidu.com"    //添加前缀地址
    }
}
复制代码
  • process.cwd():是当前执行node命令时候的文件夹地址 ——工做目录,保证了文件在不一样的目录下执行时,路径始终不变
  • __dirname:是被执行的js 文件的地址 ——文件所在目录
{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "bundle": "webpack"   //在scripts中直接运行webpack,默认如今当前目录中找,至关于 npx webpack,
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.39.0",
    "webpack-cli": "^3.3.6"
  }
}

复制代码

浅谈webpack打包输出内容


Hash: 714f926a0d1129251086
Version: webpack 4.39.0
Time: 683ms
Built at: 2019-08-02 15:52:43
  Asset      Size  Chunks /*打包的文件的id值*/            Chunk Names、/*打包的文件的名字,配置文件中entry配置的文件名 */
main.js  1.17 KiB       0  [emitted]  main
Entrypoint main = main.js
[0] ./index.js + 2 modules 480 bytes {0} [built]
    | ./index.js 100 bytes [built]
    | ./Header.js 190 bytes [built]
    | ./Footer.js 190 bytes [built]

复制代码

npm install moduleName命令

  1. 安装模块到项目node_modules目录下。
  2. 不会将模块依赖写入devDependencies或dependencies 节点。
  3. 运行 npm install 初始化项目时不会下载模块。

npm install -g moduleName命令

  1. 安装模块到全局,不会在项目node_modules目录中保存模块包。
  2. 不会将模块依赖写入devDependencies或dependencies 节点。
  3. 运行 npm install 初始化项目时不会下载模块。

npm install -save moduleName命令

  1. 安装模块到项目node_modules目录下。
  2. 会将模块依赖写入dependencies 节点。
  3. 运行 npm install 初始化项目时,会将模块下载到项目目录下。
  4. 运行npm install --production或者注明NODE_ENV变量值为production时,会自动下载模块到node_modules目录中。

npm install -save-dev moduleName命令

  1. 安装模块到项目node_modules目录下。
  2. 会将模块依赖写入devDependencies 节点。
  3. 运行 npm install 初始化项目时,会将模块下载到项目目录下。
  4. 运行npm install --production或者注明NODE_ENV变量值为production时,不会自动下载模块到node_modules目录中。

总结 devDependencies 节点下的模块是咱们在开发时须要用的,好比项目中使用的 gulp ,压缩css、js的模块。这些模块在咱们的项目部署后是不须要的,因此咱们可使用 -save-dev 的形式安装。像 express 这些模块是项目运行必备的,应该安装在 dependencies 节点下,因此咱们应该使用 -save 的形式安装。

loader是什么


webpack默认打包js文件,不认识CSS文件或者图片文件等等。

loader:打包的方案

使用Loader打包静态资源(图片篇)


file-loader

module: {
		rules: [
			{
				test: /\.(jpg|png|gif)$/,
				use: {
					loader: 'file-loader',
					options: {
                        //placeholder
						outputPath: 'images/',
						name: '[name]_[hash].[ext]'
					}
				}
			}
		]
	}
复制代码

url-loader

Name 类型 默认 描述
limit number undefined 限制文件大小
mimetype string extanme 指定文件的mimetype(不然从文件拓展名推断)
fallback string file-loader loader文件大于限制时,指定loader打包文件

将图片以base64的形式打包在js文件里。

limit :限制图片大小,若是小于这个值,打包成base64,大于这个值,和file-loader同样。

module: {
		rules: [
			{
				test: /\.(jpg|png|gif)$/,
				use: {
					loader: 'url-loader',
					options: {
                        //placeholder
						outputPath: 'images/',
						name: '[name]_[hash].[ext]',
						limit: 2048    //限制小于2048(2kb)打包成base64的形式。不然以图片的形式打包到 bundle目录下。 好处是:能够减小一些小图片的http请求数目,提高网 页加载速度
					}
				}
			}
		]
	}
复制代码

使用Loader打包静态资源(样式篇)


  • style-loader:将样式以style标签的形式,写在html文件的head中。

  • css-loader:将样式文件分析合并,打包。

  • sass-loader:将scss文件翻译成css。

  • postcss-loader:能够自动加厂商前缀 -webkit

    • 新建postcss.conifg.js

    • npm i -D autoprefixer

      module.exports = {
           plugins : [
               require('autoprefixer')({
                   overrideBrowserslist : ['last 2 versions']      //必须设置支持的浏览器才会自动添加添 加浏览器兼容
               })
           ]
       };
      复制代码

loader的使用顺序是从下到上:

{
				test: /\.(css|less|scss)$/,
				use: [
				'style-loader', 
				'css-loader', 
				'sass-loader', 
				'postcss-loader'
				]
			}
复制代码
若是要给某个loader增长配置项
{
				test: /\.(css|less|scss)$/,
				use: [
				'style-loader', 
                   {
                       loader: 'css-loader',
                       options: {
                           importLoaders: 2   //目录文件中经过import引入的文件,可能不会走sass-loader和 postcss-loader ,这个配置项为了让import引入的css文件,再过2个loader。 
                       }
                   }, 
				'sass-loader', 
				'postcss-loader'
				]
			}
复制代码

CSS Module (CSS模块化)

{
				test: /\.(css|less|scss)$/,
				use: [
				'style-loader', 
                   {
                       loader: 'css-loader',
                       options: {
                           importLoaders: 2,
                           modules: true   //CSS Module
                       }
                   }, 
				'sass-loader', 
				'postcss-loader'
				]
			}

复制代码
import style from './style.scss';
img.classList.add(style.a);   //不一样文件添加不一样的样式

复制代码

webpack打包字体文件

file-loader

使用plugins让打包更方便


plugin能够在webpack运行到某一个时刻的时候,帮你作一些事情。(相似生命周期函数)

html-webpack-plugin 会在打包结束后,自动生成一个html文件,并把打包生成的js自动引入到这个html文件中。

const htmlWebpackPlugin = require('html-webpack-plugin');
    plugins: [
      new htmlWebpackPlugin({
		  template: "./src/index.html"  //默认不会生成 <div id="dom2"></div> 须要模板
	  })
    ],

复制代码

clean-webpack-plugin 会在打包以前,删除以前打包过的文件。

const htmlWebpackPlugin = require('html-webpack-plugin');
	const {CleanWebpackPlugin} = require('clean-webpack-plugin')
    plugins: [
      new htmlWebpackPlugin({
		  template: "./src/index.html"
	  }),
      new CleanWebpackPlugin() //打包以前 webpack的output.path目录中的全部文件都将被删除一次,目录自己不会
    ],

复制代码

SourceMap的配置


1565448633280

mode: 'development';
devtool: 'cheap-module-eval-source-map';
//
mode: 'production';
devtool: 'cheap-module-source-map'

复制代码

​ source-map 生成一个map.js的文件

​ inline 将map.js文件注入到打包好的js文件

​ cheap:精确到多少行,而不关心多少列 ;不关心loader的错误;

​ module 不加module只关心本身的文件 不关心第三方文件 loader的错误映射

​ eval 以eval的形式注入到打包好的js文件中

  • source-map的原理
//todo............

复制代码

使用webpack-dev-server提高开发效率


连接

// npm i webpack-dev-server -D
	devServer: {
		contentBase: './dist',	//路径
		open: true,	//打开浏览器
		proxy: {	//代理转发 跨域
		    './api': {
		    	target: 'http://localhost:3000'
			}
        },
		port: 8081	//修改默认端口
	}
	//===========================
	 "scripts": {
    	"bundle": "webpack",
   		"start": "webpack-dev-server"
  	}

复制代码

能够结合node.js webpackmiddleware 写一个本身的webpack-dev-server

webpack-dev-server会将打包好的文件存到电脑的内存中,提高打包速度。

热模块替换(Hot Module Replacement)


需求:

  • 当须要样式局部刷新不影响页面其余内容

    1565544822334

    1565544722994

    const webpack = require('webpack');
    devServer: {
    		contentBase: './dist',
    		open: true,
    		port: 8081,
        	hot: true,   //开启HMR
        	hotOnly: true  //当HMR没有起做用,也不用刷新页面
    	}
    new webpack.HotModuleReplacementPlugin()
    
    复制代码
  • 当须要修改js文件而不影响其余的内容

    • CSS一样的配置代码不会生效,须要额外配置
    if(module.hot) {
      module.hot.accept('./demo', function () {
        demo()
        document.body.removeChild(document.getElementById('asd'))
      })
    }
    
    复制代码

使用Babel处理ES6语法


npm install --save-dev babel-loader @babel/core @babel/core是babel的核心语法库 (这个loader只是将webpack和babel作了链接,并不会处理js文件,须要借助其余模块)

npm install @babel/preset-env --save-dev

@babel/preset-env包含了全部es6-es5的规则。

{
	test: /\.js$/,
	exclude: /node-modules/,   //exclude意思是在node-modules中的js文件不会去操做
	loader: 'babel-loader',
    options: {
        presets: ['@babel/preset-env']      //还须要一个配置项
    }
}

复制代码

需求:有些低版本的浏览器不兼容一些方法,变量等,如promisemap。须要借助babel-polyfill/core-js作一个补充。

npm install @babel/polyfill已经淘汰

最新npm install core-js@3

业务代码中引入import '@babel/polyfill'/import 'core-js'

可是这么作会引入不少用不着的,如今想按需引入

这里讲一讲 useBuiltIns 配置

咱们可能在全局引入 babel-polyfill,这样打包后的整个文件体积必然是会变大的。

可是经过设置 "useBuiltIns": "usage" 可以把 babel-polyfill 中你须要用到的部分提取出来,不须要的去除。

useBuiltIns 参数说明:

  1. false: 不对 polyfills 作任何操做
  2. entry: 根据 target 中浏览器版本的支持,将 polyfills 拆分引入,仅引入有浏览器不支持的 polyfill
  3. usage(新):检测代码中 ES6/7/8 等的使用状况,仅仅加载代码中用到的 polyfills

优化

import 'core-js'
	//入口文件引入
	{
        test: /\.js$/,
        exclude: /node-modules/,
        loader: 'babel-loader',
        options: {
          presets: [['@babel/preset-env', {
            useBuiltIns: 'entry',   //若是配置为usage 就没必要引入import 'core-js'
            corejs: 3,    //必须配置依赖的corejs
            targets: {
              chrome: '70'
            }
          }]]
        }
      }

复制代码

img

注意:以上方式是写业务代码时使用的。当开发类库,组件库时若是使用这种方式会将好比promise注入到全局变量,污染全局变量

npm install --save-dev @babel/plugin-transform-runtime

npm install --save @babel/runtime

注意:@babel/runtime as a production dependency

{
        test: /\.js$/,
        exclude: /node-modules/,
        loader: 'babel-loader',
        options: {
          'plugins': [['@babel/plugin-transform-runtime', {
              "absoluteRuntime": false,
        	  "corejs": 3,   //默认值false 是不会将ES6的语法打包的
        	  "helpers": true,
        	  "regenerator": true,
        	  "useESModules": false
          }]]
        }
      }

复制代码
corejs option Install command
false npm install --save @babel/runtime
2 npm install --save @babel/runtime-corejs2
3 npm install --save @babel/runtime-corejs3

注意当文件过于复杂时:在根目录下建立.babelrc文件

配置React代码的打包


npm install @babel/preset-react

{
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": "entry",
      "corejs": 3,
      "targets": {
        "chrome": "70"
      }}],
    ["@babel/preset-react"]     // presets 中的内容是从后往前一次执行的,先执行@babel/preset-react
  ]
    "plugins": ["@babel/plugin-proposal-class-properties"]  //若是不配置没法使用箭头函数
}

复制代码

Tree Shaking概念详解


只支持ES Module(静态引入) require是动态引入 不支持!

按需引入某一模块中所须要的代码。

//好比一个文件内部暴露了两个方法,我在另外一个文件中只须要引入其中一个,可是webpack默认两个方法都会打包
mode: 'development' //默认没有开启Tree Shaking

//配置webpack.config.js,当mode为production时,不用配置这一项
{
    optimization: {
        usedExports: true
    }
}
//配置package.json
{
    "sideEffects": ["*.css","core.js"]  或者 false   //过滤不须要Tree Shaking的文件
}

复制代码

Development和Production模式的区分打包


  • sourcemap
  • 压缩代码

写两套代码

{
 "scripts": {
 	"dev": "webpack-dev-server --config webpack-dev-js",
 	"build": "webpack --config webpack-pro-js"
 }
}

复制代码

问题:

这样带来了一个问题就是两个文件中有很是多的重复代码。

解决:

新建文件夹webpack.common.js,使用webpack-merge

const merge = require('webpack-merge');
const common = require('./common')
const dev = {
	...
}
module.exports = merge(common, dev)

复制代码

Webpack和Code Splitting


optimization: {
	  splitChunks: {
       	chunks: "all",
        cacheGroups: {
            vendors: false, //前缀名
            default: false   
        }
    }
  },    // 同步代码分割 webpack.config.js的配置项

复制代码
function getComponent() {           //异步加载 代码分割 经过dynamic import
  return import(/*webpackChunkName: 'lodash'*/'lodash').then(({ default: _ }) => {
    return _
  })
}

getComponent().then( _ => {
  console.log(_.join(['a', 'b', 'c'], '%^'));
})

复制代码
{
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": "entry",
      "corejs": 3,
      "targets": {
        "chrome": "70"
      }}],
    "@babel/preset-react"
  ],
  "plugins": ["@babel/plugin-syntax-dynamic-import"]   //使用插件
}

复制代码

npm i -D @babel/plugin-syntax-dynamic-import`

/*webpackChunkName: 'lodash'*/ 这个能够给打包的文件起名字

SplitChunksPlugin 配置参数详解


Webpack和Code Splitting的底层就是是用了SplitChunksPlugin这个插件。

//SplitChunksPlugin 默认配置
splitChunks: {
    chunks: "async",    //只对异步代码进行打包 可配置all(须要配置cacheGroups ) initial(同步)
    minSize: 30000,   //大于30kb作代码分割
    minChunks: 1,	// 被用了至少多少次才进行代码分割
    maxAsyncRequests: 5,  //同时加载的模块数至多5个,超过5个,超过的不会作代码分割了
    maxInitialRequests: 3,	//入口文件最大价值3个模块,超过3个,超过的不会作代码分割了
    automaticNameDelimiter: '~',  //组,文件链接符。
    name: true,
    cacheGroups: {
        vendors: {  //打包的文件属于vendors这个组。
            test: /[\\/]node_modules[\\/]/,   //检测须要打包的库是否在node_modules中
            priority: -10,  //优先级 vendors大于default
            filename: 'vendors'  // ‘vendors.js’
        },
    	default: {	 //打包的文件属于default这个组。
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true   //若是一个模块被打包过 a,b,c c里面引入了a,b a里面又引入了b,则c直 接复用以前打包过的b,而不须要再次打包b
        }
    }
}
//同步代码打包的过程当中,知足前面的配置要求,不会直接作代码分割,而是放入cacheGroups中保存,在作判断是放入哪一个组里
//异步代码不用

复制代码

Lazy Loading懒加载,Chunk是什么?


import语法

每个打包出来的文件都是一个chunk

打包分析,Preloading,Prefetching


webpack --profile --json > stats.json github.com/webpack/ana…

代码分析

浏览器查看代码使用率:network + ctrl + shift + p

webpack推荐多使用 异步加载的代码 import().then()

利用缓存提高页面性能是很是有限的,利用webpack的懒加载 + prefetch 极大的提高代码利用率,是很是好的提高页面性能的方案。

某些异步加载的代码,当首页彻底加载出来后,此时带宽彻底释放后,能够进行预加载。

prefetch

1568643034126

prefetch和preload的区别:

  • prefetch会等主代码彻底加载后带宽空闲时加载;
  • preload会跟着主代码一块儿预加载。

CSS的代码分割


entry: {
    main: './src/index.js'
}
output: {
    filename: "[name].js",    //main.js为主文件入口,走的filename这个配置项;
    chunkFilename: "[name].chunk.js" //main.js中的按需加载的文件,走这个配置项。
}

复制代码

css的代码分割须要借助插件:

MiniCssExtractPlugin 缺点:不支持HMR,适合线上环境作打包。

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
use: [
    // 'style-loader',
    MiniCssExtractPlugin.loader,   //替换成这个。
    {
        loader: "css-loader",
        options: {
            importLoaders: 2
        }
    },
    'sass-loader',
    'postcss-loader'
    ]

复制代码

optimize-css-assets-webpack-plugin 压缩打包后的css代码。

loading....

相关文章
相关标签/搜索