webpack
1.基础知识
1.1什么是webpack
WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。
可以做的事:
代码转化、文件优化、代码分割、模块合并、自动刷新、代码校验、自动发布
1.2需要提前掌握内容
- 需要node基础、以及npm的使用
- 掌握es6语法
2.webpack的基本配置
2.1Webpack安装
yarn add webpack webpack-cli -D
或者npm install webpack webpack-cli -D
2.3打包
npx webpack
2.4.手动配置webpack
webpack.config.js
let path = require('path');
const config = {
mode:'development',//模式 两种:development,production
//入口
entry: './path/to/my/entry/file.js'
//出口
output:{
filename:'bundle.[hash:8].js',//打包后的文件名 hash每次打包产生新的文件,hash:8 表示哈希值只限制8位
path: path.resolve(__dirname,'dist') //路径必须是一个绝对路径
}
};
module.exports = config;
entry
属性的单个入口语法,是下面的简写:
const config = {
mode:'development',//模式 两种:development,production
//入口
entry: {
main: './path/to/my/entry/file.js'
},
//出口
output:{
filename:'bundle.js',//打包后的文件名
path: path.resolve(__dirname,'dist') //路径必须是一个绝对路径
}
};
2.5.添加脚本scripts(package.json)
{
"name": "lesson01",
"version": "1.0.0",
"description": "NULL",
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "webpack"
},
"dependencies": {
"webpack": "^4.41.2",
"webpack-cli": "^3.3.9"
},
"devDependencies": {
"css-loader": "^3.2.0",
"html-webpack-plugin": "^3.2.0",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.8.0",
"style-loader": "^1.0.0",
"webpack-dev-server": "^3.9.0"
}
}
3.Html插件
3.1 开启web服务器热更新
-
安装:
yarn add webpack-dev-server -D
-
再package.json中注入脚本,
"scripts": { "build": "webpack", "dev": "webpack-dev-server" }
此时直接运行:
npm run dev
web服务器就启动好了(此时访问localhost:8080还访问不到index.html) -
还需配置webpack.config.js
let path = require("path") module.exports = { mode:"development", entry:"./src/index.js", output:{...}, devServer:{ port:3000, //自定义端口号 contentBase:'./static' //打包文件所放置的目录 } }
3.2 html-webpack-plugin
插件的基本作用就是生成html文件。原理很简单:
将 webpack中`entry`配置的相关入口thunk 和 `extract-text-webpack-plugin`抽取的css样式 插入到该插件提供的`template`或者`templateContent`配置项指定的内容基础上生成一个html文件,具体插入方式是将样式`link`插入到`head`元素中,`script`插入到`head`或者`body`中。
先安装:yarn add html-webpack-plugin -D
实例化该插件时可以不配置任何参数,例如下面这样:
//webpack.config.js
var HtmlWebpackPlugin = require('html-webpack-plugin')
webpackconfig = {
...
plugins: [ //数组 存放着webpack所有的插件
new HtmlWebpackPlugin({
//打包模板
template:'./src/index.html',
//打包出来的文件名字
filename:'index.html',
minfy:{ //最小化操作
removeAttributeQuotes:true, //删除属性双引号
collapseWhitespace:true, //删除空格以及换行
}
})
]
}
3.3 webpack解析CSS模块
3.3.2样式处理(一)
解析出来的css是放在
安装:yarn add style-loader css-loader -D
//a.css
body{
background:pink
}
//index.css
@import './a.css'
body{
color:blue
}
//index.js
require("./index.css")
配置模块,module
let path = require("path")
let HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
mode:"development",
entry:"./src/index.js",
output:{...},
devServer:{...},
plugins:[...],
module:{ //模块
rules:[ //规则
//css-loader:解析@import这种语法的
//还有很多的loader:less-loader stylus-loader
//style-loader:把.css插入到head标签中
/*
loader特点:希望单一
loader用法:字符串只能用一个loader,多个loader则需要数组[],还可以写成对象方式
loader的执行顺序:默认是从右向左,从下至上执行
*/
{
test:/\.css$/,
use:[
{
loader:"style-loader",
},
"css-loader"
]
}
]
}
}
3.3.2 样式处理(二)
解析出来的css是放在通过标签引入
安装:yarn add mini-css-extract-plugin -D
//webpack.config.js
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: 'main.css', //抽离出来的css样式的名字
}),
],
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
'css-loader',
],
}
]
}
}
-
自动为css样式列表添加前缀:
-
安装
yarn add postcss-loader autoprefixer -D
-
添加配置文件post.config.js
//post.config.js module.exports = { plugins:[ require("autoprefixer") ] }
//webpack.config.js let MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { plugins: [ new MiniCssExtractPlugin({ filename: 'main.css', //抽离出来的css样式的名字 }), ], module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader }, 'css-loader', 'postcss-loader' ], } ] } }
-
-
优化项CSS、JS
-
安装:
-
压缩css:
yarn add optimize-css-assets-webpack-plugin -D
-
压缩js:在webpack4之前,默认集成该插件,无需安装
在webpack4之后,需要独立安装安装:
npm install uglifyjs-webpack-plugin --save-dev
另外,webpack4默认是生产环境,自带js压缩功能
如果要关闭,需要设置mode为development
//webpack.config.js
-
-
let OptimizeCssAssetsPlugin = require(‘optimize-css-assets-webpack-plugin’);
module.exports = {
...,
optimization: {
minimizer: [
new OptimizeCssAssetsPlugin({
cssProcessorOptions: {
mergeLonghand: false,
discardComments: { removeAll: true }
},
canPrint: true,
})
],
},
...
};
```
4.处理JS模块(babel)
4.1 转换ES6语法
es6或者更高的语法转化为es5
首先安装:npm install -D babel-loader @babel/core @babel/preset-env webpack
,处理一般语法,
然后安装:npm install -D @babel/plugin-proposal-class-properties
,处理类似class类的语法
最后安装装饰器:``,处理类 @log
-
被压缩js文件
//index.js let str = require("./a.js"); console.log(str); require("./index.css") let fn = () =>{ console.log("babel"); } fn(); //es6更高级的语法 @log class A{ a = 1; } let a = new A(); console.log(a.a); function log(target) { console.log(target); }
-
添加匹配规则
//webpack.config.js let path = require("path") let HtmlWebpackPlugin = require("html-webpack-plugin") let TerserJSPlugin = require('terser-webpack-plugin'); let MiniCssExtractPlugin = require('mini-css-extract-plugin'); let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); module.exports = { mode:"development", entry:"./src/index.js", output:{... }, devServer:{...}, plugins:[...], optimization: {...}, module:{ rules:[ { test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'], plugins:[ [ '@babel/plugin-proposal-decorators',{"legacy":true}], ['@babel/plugin-proposal-class-properties',{"loose":true}] ] } } } ] } };
4.2 校验语法规范
安装: yarn add eslint eslint-loader
添加规则:
//webpack.config.js
......
module.exports = {
mode:"development",
entry:"./src/index.js",
output:{...},
devServer:{...},
plugins:[...],
optimization: {...},
module:{
rules:[ //运行规则:从下至上执行
{
test:/\.js$/,
use:{
loader:"eslint-loader",
options:{
enforce:"pre" //强制首先执行
}
}
},
{
test:/\.css$/,
use:[
{
// loader:"style-loader",
loader: MiniCssExtractPlugin.loader
},
"css-loader",
'postcss-loader'
]
},
....
]
}
};
添加规则生成 .eslintrc.json 文件 https://eslint.org/demo
下载后放到项目更目录下,注意:下载下来的名称是:eslintrc.json,我们要自己改名为:.eslintrc.json
此后再进行打包,打包过程中就会校验语法规则是否正确,并打印在控制台。
4.3 全局变量引入问题
-
暴露全局loader、内联loader:expose-loader 暴露到window上
安装:
yarn add expose-loader -D
实例:向window中暴露JQuery中的$
首先安装Jquery:
yarn add jquery
其次导入
// index.js import $ from "jquery"; console.log(window.$);
最后匹配规则:
//webpack.config.js rules:[ { test:require.resolve("jquery"), use:"expose-loader?$" } ]
-
在每个模块中注入$对象,需要靠webpack一个插件 ProvidePlugin
//webpack.config.js let webpack
-
直接引入
<!--src/index.html--> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
5.图片处理
webpack打包图片:
5.1在JS中创建图片来引入
file-loader 默认会在内部生成一张图片 到build目录下,把生成图片的名字返回回来
-
此时安装:
yarn add file-loader -D
和yarn add url-loader -D
-
index.js
//index.js import logo from "./logo.jpg"; //把图片引入,返回的结果是一个新的图片地址 /* file-loader 默认会在内部生成一张图片 到build目录下,把生成图片的名字返回回来 */ let image = new Image(); console.log(image); image.src = logo; document.body.appendChild(image);
-
配置规则
rules:[ { test:/\.(png|jpg|gif)$/, //做一个限制,当我们的图片小于多少K时,用base64来转化 //否则用file-loader产生真实loader use:{ loader:'url-loader', options:{ limit:30*1024 } } } ]
5.2 在css中引入,background:(url)
-
在index.css添加图片
body{ background: pink; /*transform: rotate(180deg);*/ } h1{ width: 150px; height: 100px; background: url("logo.jpg"); background-size: cover; }
-
在index.js中导入css
import './index.css'
5.3 在html中引入,< img src="" />
html-withimg-loader:解析html,编译图片
-
安装:
yarn add html-withimg-loader -D
-
同样匹配规则:
//webpack.config.js rules:[ //运行规则:从下至上执行 { test:/\.html$/, use:'html-withimg-loader' } ]
-
html引入图片
<!--src/index.html--> <img src="./logo.jpg" />
运行 npm run dev,即可
6.打包文件分类
如果需要给所有的资源添加publicPath,则需要
//webpack.config.js
module.exports = {
mode:"development",
entry:"./src/index.js",
output:{
filename:"bundle.[hash:5].js",
path:path.resolve(__dirname,"static"),
publicPath:'http://www.example.com'
},
...
}
6.1 打包img
只需在匹配规则中rules:添加
{
test:/\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
//做一个限制,当我们的图片小于多少K时,用base64来转化
//否则用file-loader产生真实loader
use:{
loader:'url-loader',
options:{
limit:8192,
outputPath:'/images/',
publicPath:'http://www.example.com'
},
}
},
6.2 打包css
只需在插件中:
new MiniCssExtractPlugin({
//抽离出来的css样式的名字
filename: 'css/main.css',
}),
7.打包多页应用
重新创建项目,并初始化项目:
yarn init -y
安装相关插件:
yarn add webpack webpack-cli -D
yarn add html-webpack-plugin -D
创建js文件:src/index.js,src/other.js,webpack.config.js
创建HTML文件:src/home.html,src/other.html
配置文件:
//webpack.config.js
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: 'development',
entry: {
home: './src/index.js',
other: './src/other.js'
},
output: {
filename: '[name].js',
path: path.resolve( __dirname, 'dist' )
},
plugins: [
new HtmlWebpackPlugin ({
template: './index.html',
filename: 'home.html',
chunks: ['home'] //单独引入到对应的html中
}),
new HtmlWebpackPlugin ({
template: './index.html',
filename: 'other.html',
chunks: ['other']
})
]
}
8.配置source-map
作用:标识报错
-
源码映射 会单独生成一个sourcemap文件,出错时,会标识当前报错的列和行,特点:大而全devtool:“source-map”,
-
不会产生单独的文件,但是可以显示行和列 :devtool:“eval-source-map”,
-
不会产生列,但是可以生成一个单独的映射文件 :devtool:“cheap-module-source-map”,
-
不会打包文件,集成在打包的文件中,不会产生列 :devtool:“cheap-module-eval-source-map”,
//webpack.config.js
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: 'development',
entry: {
home: './src/index.js'
},
module:{
rules:[
{
test:/\.js$/,
use:{
loader:"babel-loader",
options:{
presets:["@babel/preset-env"]
}
}
}
]
},
//源码映射 会单独生成一个sourcemap文件,出错时,会标识当前报错的列和行,特点:大而全
devtool:"source-map",
output: {
filename: '[name].js',
path: path.resolve( __dirname, 'dist' )
},
plugins: [
new HtmlWebpackPlugin ({
template: './index.html',
filename: 'home.html',
})
]
};
9.watch的用法
随着项目大了,后端与前端联调,我们不需要每一次都去打包,这样特别麻烦,我们希望的场景是,每次按保存键,webpack自动为我们打包,这个工具就是watch!因为watch是webpack自带的插件,所以我们只需要配置就行了
//webpack.config.js
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: 'development',
entry: {
home: './src/index.js'
},
module:{
rules:[
{
test:/\.js$/,
use:{
loader:"babel-loader",
options:{
presets:["@babel/preset-env"]
}
}
}
]
},
watch:true,
watchOptions:{ //监控选项
poll:1000, //每秒问我1000次
aggregateTimeout:500, //防抖,500秒后打包一次,防止我每输一个子就打包一次
ignored:/node_modules/ //不需要监控哪个文件
},
output: {
filename: '[name].js',
path: path.resolve( __dirname, 'dist' )
},
plugins: [
new HtmlWebpackPlugin ({
template: './index.html',
filename: 'home.html',
})
]
};
10.webpack的小插件
-
clean-webpack-plugin
-
作用:在每次生成dist目录前,先删除本地的dist文件
-
安装:
yarn add clean-webpack-plugin -D
-
代码:
//webpack.config.js let {CleanWebpackPlugin} = require("clean-webpack-plugin"); ..... output: { filename: '[name].js', path: path.resolve( __dirname, 'dist' ) }, plugins: [ //官方介绍,要删除的正是output.path,output配置项里的路径就是dist目录 new CleanWebpackPlugin() ]
-
-
copy-webpack-plugin
-
作用:在webpack中拷贝文件和文件夹
-
安装:
yarn add copy-webpack-plugin -D
-
代码:
//webpack.config.js let CopyPlugin = require('copy-webpack-plugin'); .... plugins: [ new CopyPlugin([ {from:"source/source.html",to:"./"}, ]) ]
-
-
banner-plugin
-
作用:在输出的打包压缩的JS代码内嵌入版权识别注释。
-
安装:不必安装,webpack内置插件
-
代码:
//webpack.config.js let webpack = require("webpack"); ..... plugins: [ new webpack.BannerPlugin("make 2019-11-7 22:12:00") ]
-
11.webpack跨域问题
配置了一个代理服务器
//webpack.config.js
module:{...},
devServer:{
proxy:{ //重新的方式,把请求代理到express服务器上
//配置了一个代理服务器
'/api':{
target:"http://localhost:8000",
//目的是去掉访问时候的 '/api'
pathRewrite:{'/api':''}
}
}
},
output: {...},
12.resolve属性的配置
webpack在启动后会从配置的入口模块触发找出所有依赖的模块,Resolve配置webpack如何寻找模块对应的文件。webpack内置JavaScript模块化语法解析功能,默认会采用模块化标准里约定好的规则去寻找,但你可以根据自己的需要修改默认的规则。
本实例首先安装css-loader、style-loader\bootstrap
//webpack.config.js
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
let { CleanWebpackPlugin } = require("clean-webpack-plugin");
let CopyPlugin = require("copy-webpack-plugin");
let webpack = require("webpack");
module.exports = {
mode: "development",
entry: {
home: "./src/index.js"
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"]
}
}
}
]
},
resolve: {
//解析 第三方包common
modules: [path.resolve("node_modules"), path.resolve("dist")], //指定在某个目录下找
// alias: {
// //别名 目的是再index.js中引入第三方模块时候,简化代码 import "bootstrap";
// bootstrap: "bootstrap/dist/css/bootstrap.css"
// },
// 或者通过mainFilds来简化操作
mainFields: ["style", "main"],
//入口文件的名字 index.js
//mainFiles: []
// 目的是再index.js中引入自己写的css时,简化代码 import "./style";
extensions: [".js", ".css", ".json"]
},
devServer: {
proxy: {
//重新的方式,把请求代理到express服务器上
//配置了一个代理服务器
"/api": {
target: "http://localhost:8000",
//目的是去掉访问时候的 '/api'
pathRewrite: { "/api": "" }
}
}
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new HtmlWebpackPlugin({
template: "./index.html",
filename: "index.html"
})
]
};
// src/index.js
import "bootstrap";
import "./style";
console.log("index");
class Log {
constructor() {
console.log("出错了!!!!");
}
}
let log = new Log();
// src/index.html
<button class="btn btn-success">点击点击点击</button>
// src/style.css
body {
background-color: yellow;
}
button {
width: 300px;
display: inline-block;
margin: 100px;
}
最后启动npm run dev
13.定义环境变量
用于创建编译时 “配置的全局常量” 以方便进行development和production环境转换
// src/index.js
let url = "";
if (DEV === "dev") {
url = "http://localhost:8080";
} else {
url = "http://www.lyxself.xyz";
}
console.log("url:" + url);
console.log(typeof FLAG);
console.log(EXPORESSON);
// webpack.config.js
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
let webpack = require("webpack");
module.exports = {
mode: "development",
entry: {
home: "./src/index.js"
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"]
}
}
}
]
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist")
},
plugins: [
//用于创建编译时 “配置的全局常量” 以方便进行development和production环境转换
new webpack.DefinePlugin({
DEV: JSON.stringify("production"),
FLAG: "true",
EXPORESSON: "1+1"
}),
new HtmlWebpackPlugin({
template: "./index.html",
filename: "index.html"
})
]
};
14.区分production和development不同环境
-
首先重新创建webpack配置文件:
- webpack.base.js : 用于放置production和development共同的环境配置
- webpack.dev.js : 用于放置development环境配置
- webpack.prod.js : 用于放置production环境配置
-
其次安装插件:
yarn add webpack-merge -D
-
然后代码
-
webpack.dev.js
let {smart} = require("webpack-merge"); let base = require("./webpack.base.js"); module.exports = smart(base,{ mode:"development", })
-
webpack.prod.js
let {smart} = require("webpack-merge"); let base = require("./webpack.base.js"); module.exports = smart(base,{ mode:"production", })
-
-
最后运行不同的打包命令
npm run build -- --config webpack.dev.js
查看dist/index.js 发现并没有被压缩npm run build -- --config webpack.prod.js
查看dist/index.js 发现已经被压缩
15.优化
15.1.noParse
webpack精准过滤不需要解析的文件
// webpack.config.js
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
modules: {
//不去解析Jquery中的依赖库
noParse: /jquery/,
rules: [
{
test: "/.js$/",
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"]
}
}
}
]
},
output: {
filename: "boundle.[hash:4].js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html"
})
]
};
15.2.IgnorePlugin
忽略第三方包指定目录,让这些指定目录不要被打包进去
安装moment库:yarn add moment
15.3.动态链接库dllPlugin
Webpack构建时接入动态链接库的思想后,会大大提升构建速度。 原因在于包含大量复用模块的动态链接库只需要编译一次,在之后的构建过程中被动态链接库包含的模块将不会在重新编译,而是直接使用动态链接库中的代码。 由于动态链接库中大多数包含的是常用的第三方模块,例如 react、react-dom,只要不升级这些模块的版本,动态链接库就不用重新编译。
15.4.多线程打包(项目较大时使用)
由于有大量文件需要解析和处理,构建是文件读写和计算密集型的操作,特别是当文件数量变多后,Webpack 构建慢的问题会显得严重。 运行在 Node.js 之上的 Webpack 是单线程模型的,也就是说 Webpack 需要处理的任务需要一件件挨着做,不能多个事情一起做。
happypack 是 webpack 的一个插件,目的是通过多进程模型,来加速代码构建 。 分解任务和管理线程的事情 HappyPack 都会帮你做好,你所需要做的只是接入 HappyPack。
安装:yarn add happypack
//webpack.config.js
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
let Happypack = require("happypack");
module.exports = {
mode: "development",
entry: "./src/index.js",
module: {
//不去解析Jquery中的依赖库
noParse: /jquery/,
rules: [
{
test: "/.m?js$/",
exclude: /(node_modules|bower_components)/,
include: path.resolve("src"),
use: "Happypack/loader?id=js"
},
{
test: "/.css$/",
exclude: /(node_modules|bower_components)/,
include: path.resolve("src"),
use: "Happypack/loader?id=css"
}
]
},
output: {
filename: "boundle.[hash:4].js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new Happypack({
id: "css",
use: ["style-loader", "css-loader"]
}),
new Happypack({
id: "js",
use: [
{
loader: "babel-loader",
opations: {
presets: ["@babel/preset-env", "@babel/preset-react"]
}
}
]
}),
new HtmlWebpackPlugin({
template: "./public/index.html"
})
]
};
打包:npx webpack
控制台会有:Happy[js]: Version: 5.0.1. Threads: 3
开启了三个线程
16.webpack自带优化
-
tree-shaking
- import导入,在生产环境下 会自动除掉没有用的代码
- es6模块会把结果放到default上
-
scope hosting 作用域提升,在webpack中自动省略可简化的代码
-
let a = 1; let b = 2; let c = 3; let d = a+b+c; console.log(d+"...............");
-
17.抽取公共代码
17.1.为什么需要提取公共代码
大型网站通常会由多个页面组成,每个页面都是一个独立的单页应用。 但由于所有页面都采用同样的技术栈,以及使用同一套样式代码,这导致这些页面之间有很多相同的代码。
如果每个页面的代码都把这些公共的部分包含进去,会造成以下问题:
- 相同的资源被重复的加载,浪费用户的流量和服务器的成本;
- 每个页面需要加载的资源太大,导致网页首屏加载缓慢,影响用户体验。
如果把多个页面公共的代码抽离成单独的文件,就能优化以上问题。 原因是假如用户访问了网站的其中一个网页,那么访问这个网站下的其它网页的概率将非常大。 在用户第一次访问后,这些页面公共代码的文件已经被浏览器缓存起来,在用户切换到其它页面时,存放公共代码的文件就不会再重新加载,而是直接从缓存中获取。 这样做后有如下好处:
- 减少网络传输流量,降低服务器成本;
- 虽然用户第一次打开网站的速度得不到优化,但之后访问其它页面的速度将大大提升。
17.2.如何提取公共代码
你已经知道了提取公共代码会有什么好处,但是在实战中具体要怎么做,以达到效果最优呢? 通常你可以采用以下原则去为你的网站提取公共代码:
- 根据你网站所使用的技术栈,找出网站所有页面都需要用到的基础库,以采用 React 技术栈的网站为例,所有页面都会依赖 react、react-dom 等库,把它们提取到一个单独的文件。 一般把这个文件叫做
base.js
,因为它包含所有网页的基础运行环境; - 在剔除了各个页面中被
base.js
包含的部分代码外,再找出所有页面都依赖的公共部分的代码提取出来放到common.js
中去。 - 再为每个网页都生成一个单独的文件,这个文件中不再包含 base.js 和 common.js 中包含的部分,而只包含各个页面单独需要的部分代码。
文件之间的结构图如下:
17.3.如何通过 Webpack 提取公共代码
-
抽离自己的js文件和抽离第三方模块
//a.js console.log("this is A javascript"); //b.js console.log("this is B javascript"); //c.js import $ from "jquery"; console.log($); //index.js import "./a"; import "./b"; import $ from "jquery"; console.log("this is Index Javascript"); console.log($); //other.js import "./a"; import "./b"; import $ from "jquery"; console.log("this is Other Javascript"); console.log($);
//webpack.config.js let path = require("path"); let HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { optimization: { //分割代码块 splitChunks: { // 缓存组 cacheGroups: { // 公共的模块 common: { chunks: "initial", minSize: 0, minChunks: 2 }, //抽离第三方模块 vendor: { priority: 1, //增加权重, test: /node_modules/, //只要引入node_modules里面的模块,我就把你抽离出来 chunks: "initial", minSize: 0, minChunks: 2 } } } }, mode: "production", entry: { index: "./src/index.js", other: "./src/other.js" }, module: { rules: [ { test: /\.js$/, use: { loader: "babel-loader", options: { presets: ["@babel/preset-env"] } } } ] }, output: { filename: "[name].js", path: path.resolve(__dirname, "dist") }, plugins: [ new HtmlWebpackPlugin({ template: "./public/index.html" }) ] };
18.webpack中的懒加载
官方:懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。
通俗来说就是,如果每次加载页面的时候都会某些代码块,会重复的请求,造成资源的浪费,影响网站性能。所以提出了懒加载的解决办法:按需加载,初始化不需要加载此代码块,等到具体的执行需要时候,再加载,从而优化性能。
// src/source.js
export default "Hello Webpack!!";
// src/index.js
let button = document.createElement("button");
button.innerHTML = "点击我";
button.addEventListener("click", function() {
// es6草案中的语法 jsonp实现动态加载文件
import("./source.js").then(data => {
console.log(data.default);
});
});
document.body.appendChild(button);
19.webpack中的热更新
开发环境页面热更新早已是主流,常见的需求如赛事网页推送比赛结果、网页实时展示投票或点赞数据、在线评论或弹幕、在线聊天室等,都需要借助热更新功能,才能达到实时的端对端的极致体验。
webpack的热更新又称热替换(Hot Module Replacement),缩写为HMR。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
//webpack.config.js
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
let webpack = require("webpack");
module.exports = {
mode: "production",
entry: {
index: "./src/index.js"
},
devServer: {
hot: true, //启用热更新
open: true
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"]
}
}
}
]
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html"
}),
new webpack.NamedModulesPlugin(), //打印更新模块路径
new webpack.HotModuleReplacementPlugin() //热更新插件
]
};
// src/index.js
import source from "./source";
console.log(source);
if (module.hot) {
module.hot.accept("./source.js", () => {
let str = require("./source");
console.log(str + "---文件更新了!!");
});
}
// src/source.js
export default "Hello Webpack!!This is first Loading!!This is Second Loading!!";