webpack

webpack 简介

webpack是一个静态模块打包器,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
这里将的webpack的版本是: wepack:4.41.0

安装

yarn add webpack webpack-cli --dev

命令使用 webpack --config config文件路径 --mode=development(production)
一般加入在package.jons的script,使用yarn build命令

"scripts": {
    "build": "webpack --config  webpack.config.js  --mode=development"
  },

webpack 核心概念

entry 入口

entry是创建依赖关系图的起点,起点可以是单个文件,也可以是多个文件

  • 单个文件
module.exports = {
	entry: "src/index.js"
}

或者

module.exports = {
	entry: {
		main: "./src/index.js"
	}
}
  • 多个文件
module.exports = {
	entry: {
		app: "./src/app.js",
		vendor: "./src/vendor.js"
	}
}

出口 output

output告诉webpack在哪里输出文件

  • 基本语法
module.exports = {
	output: {
		filename: "文件名",
		path: "最后文件输出的路径"
	}
}
  • 单个入口的output
module.exports = {
	entry: "./src/index.js",
	output: {
		filename: "bundle.js",
		path: path.resolve(__dirname,"dist")
	}
}
  • 多个入口的output
module.exports = {
	entry: {
		app: "./src/app.js",
		vendor: "./src/vendor.js"
	},
	output: {
		filename: "[name].bundle.js",
		path: path.resolve(__dirname, "dist")
	}
}

loader

loader将那些非javascript文件转化为webpack能处理的模块

基本语法
  1. 在配置文件中指出
module.exports = {
	module: [
		{
			test : /\.css$/, // 正则指出需要转化的那些文件
			use: ["style-loader","css-loader"]  // 指出转化用到的loader
			// use 也可指定option
			use: {
            	loader: 'css-loader',
            	options: {
              		modules: true   // 这里为loader的参数
            	}
          }
		}
	]
}
  1. 内联
import Styles from 'style-loader!css-loader?modules!./styles.css';
  1. cli
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
常用的loader
style-loader 与 css-loader (官网建议一起用)

用于将css插入dom中, 需要先yarn add style-loader css-loader --dev, 配置文件语法如下

module.exports = {
	module: [
		rules: [
		test: /\.css$/,
		use: ["style-loader","css-loader"]]
	]
}

在引入css的时候,用import/require() ,会在最终生成的js里面动态插入到head 到style标签中,如下图:
在这里插入图片描述
这里需要注意一点:(1)loader 是从右到做执行到,webpack在打包时遇到import/require到css文件会先利用css-loader去解析css文件,然后再利用style-loader把解析生成到css动态插入到head的script标签里面。(因此style-loader和css-loader是有顺序的)

less-loader

解析less语法为css语法,首先需要安装yarn add less less-loader style-loader css-loader,配合一起使用,来解析css文件,配置文件语法如下:

module.exports = {
    ...
    module: {
        rules: [{
            test: /\.less$/,
            use: [{
                loader: "style-loader" // creates style nodes from JS strings
            }, {
                loader: "css-loader" // translates CSS into CommonJS
            }, {
                loader: "less-loader" // compiles Less to CSS
            }]
        }]
    }
};

结果如下:
less 文件为
在这里插入图片描述
最后打包运行后的less 被插入到了style标签中
在这里插入图片描述

sass-loader

sass-loader是把sass语法编译为css ,首先需要安装 yarn add sass-loader node-sass style-loader css-loader --dev

module.exports = {
  ...
  module: {
    rules: [{
      test: /\.scss$/,
      use: [{
          loader: "style-loader" // 将 JS 字符串生成为 style 节点
      }, {
          loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
      }, {
          loader: "sass-loader" // 将 Sass 编译成 CSS
      }]
    }]
  }
};

其中会有node-sass 安装出现问题的情况,需要在.npmrc指定镜像

sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
file-loader

import require的图片引入到模块中
配置

module.exports = {
  ...
  module: {
    rules: [
      {
        test: /.(png|svg|jpg|git)$/,
        use: [
          {
            loader: "file-loader",
            options: {
              outputPath: "images/"  //   最后输出到图片到相对位置
            }
          }
        ]
      }
    ]
  }
};

最后图片被打包到 /dist/images目录中
也可用于加载字体

 {
    test: /\.(woff|woff2|eot|ttf|otf)$/,
     use: [          
      	'file-loader'
     ]
}
csv-loader xml-loader

加载数据

module.exports = {
   ....
    module: {
      rules: [
       {
         test: /\.(csv|tsv)$/,
         use: [
           'csv-loader'
         ]
       },
       {
         test: /\.xml$/,
         use: [
           'xml-loader'
         ]
       }
      ]
    }
  };

mode 模式

值有development 或 production,告诉webpack启用那个模式下的优化配置

module.exports = {
  mode: 'production'
};

pluign 插件

本质

webpack 插件是一个具有 apply 属性的 JavaScript 对象。apply 属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。
例如下面就是个简单的pluign例子

const pluginName = 'ConsoleLogOnBuildWebpackPlugin';  // 应该是驼峰式命名的插件名称

class ConsoleLogOnBuildWebpackPlugin {
    apply(compiler) {
        compiler.hooks.run.tap(pluginName, compilation => {
            console.log("webpack 构建过程开始!");
        });
    }
}
用法
module.exports =  {
	...
	plugins: [new ConsoleLogOnBuildWebpackPlugin()]
}
常用插件
HtmlWebpackPlugin
  1. 安装 yarn add html-webpack-plugin --dev
  2. 作用 自动生成一个html文件,并把打包好的需要的script都加进去
  3. 语法
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');

var webpackConfig = {
  entry: 'index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
  plugins: [new HtmlWebpackPlugin({
  	title: "hello world"
	})]
};
  1. 参数
  • title html 的title
  • filename 生成的文件名 默认是index.html
  • favicon 图标
  • template index.html 路径
CleanWebpackPlugin
  1. 安装 yarn add clean-webpack-plugin --dev
  2. 使用
  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
 const { CleanWebpackPlugin } = require("clean-webpack-plugin");

  module.exports = {
    entry: {
      app: './src/index.js',
      print: './src/print.js'
    },
    plugins: [
      new CleanWebpackPlugin(),  // 每次打包前清空dist文件加
      new HtmlWebpackPlugin({
        title: 'Output Management'
      })
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

模块

webpack 支持的模块
  • ES2015 import 语句
  • CommonJS require() 语句
  • AMD define 和 require 语句
  • css/sass/less 文件中的 @import 语句。
  • 样式(url(…))或 HTML 文件(<img src=...>)中的图片链接(image url)
模块解析

使用enhanced-resolve解析路径,支持3中解析规则方式

  • 绝对路径
  • 相对路径
  • 模块路径

其它配置

devtool

此选项控制是否生成,以及如何生成 source map。
devtool: “inline-source-map” 只在开发环境中用于排查错误在源码中

webpack --watch

如果其中一个文件被更新,代码将被重新编译,所以你不必手动运行整个构建。

webpack-dev-server

起一个web服务器而且实时加载

  1. 安装 yarn add webpack-dev-server --dev
  2. 在插件中配置
module.exports =  {
	...
	devServer: {
		contentBase: "./dist"
	}
}
  1. 在package.json 加入
{
	"script" : {
		"start": "webpack-dev-server --open",
	}
}
  1. 属于命令yarn start 会打开一个localhost:8080页面
HRM 热模块替换
  • 开启
{
	devServer: {
		hot: true
	}
}
  • 使用
import print from "./print";
function component() {
  const element = document.createElement("div");
  const btn = document.createElement("button");

  element.innerHTML = "hello word, threejs";

  btn.innerHTML = "click me to print";
  btn.onclick = print;

  element.appendChild(btn);

  return element;
}

let element = component(); // 当 print.js 改变导致页面重新渲染时,重新获取渲染的元素
document.body.appendChild(element);

if (module.hot) {
  module.hot.accept("./print.js", () => {
    console.warn("Acceptiong th updated printMe module");
    document.body.removeChild(element);
    element = component(); // 重新渲染页面后,component 更新 click 事件处理
    document.body.appendChild(element);
  });
}
tree shaking

通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)

  1. 配置
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  mode: 'development',
  optimization: {
   usedExports: true,
  },
};

build后会看到,在这里插入图片描述
没有用到的方法squre被标示出来了,但是没有删除掉
2. 删除无用的代码
在上面的配置 加上minimize: true

optimization: {
   usedExports: true,
   minimize: true
  },

在这里插入图片描述
可以看到square以及被移除
3. 在生产环境中 ,只需要指定mode:"production"就会自动tree shaking 同时缩小文件体积

开发环境配置

开发环境和生成环境的性能要求区别:
(1)开发环境:要求实时刷新和source map
(2) 生产环境: 要求更小的bundle 以及更优化的资源,以改善加载时间
因此开发环境和生产环境的配置需要分开

  • 将两个环境公用动配置放在webpack/common.webapck.js 文件中,代码如下:
    主要是定义入口出口和loader 以及plugin
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: {
    app: "./src/index.js"
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: "./index.html",
      favicon: "./favicon.ico"
    })
  ],
  output: {
    filename: "[name].bundle.js",
    path: path.resolve(__dirname, "dist")
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
      },
      {
        test: /\.less$/,
        use: ["style-loader", "css-loader", "less-loader"]
      },
      {
        test: /\.scss$/,
        use: ["style-loader", "css-loader", "sass-loader"]
      },
      {
        test: /\.(png|jpg|svg|gif)$/,
        use: [
          {
            loader: "file-loader",
            options: {
              outputPath: "images/"
            }
          }
        ]
      }
    ]
  }
};

  • 开发环境的配置,需要用到webpack-merge去merge 两者的配置,webpack/webpack.dev.js的代码如下
    除了common中有的,加入了tree shaking 和 source-map
const merge = require("webpack-merge");

const common = require("./webpack.common");
const ConsoleLogOnBuildWebpackPlugin = require("../src/example.plugin");

module.exports = merge(common, {
  mode: "development",
  devtool: "inline-source-map",
  devServer: {
    contentBase: "../dist",
    hot: true
  },
  optimization: {
    usedExports: true,
    minimize: true
  },
  plugins: [new ConsoleLogOnBuildWebpackPlugin()]
});

  • 生产环境的配置,同样需要webpack-merge,代码如下:
    mode: “production”, 就自带了tree shaking和代码压缩 , 同时把出口文件加上hash,每次生成不一样,放置缓存
const path = require("path");
const merge = require("webpack-merge");

const common = require("./webpack.common");

module.exports = merge(common, {
  mode: "production",
  output: {
    filename: "[name].[hash].bundle.js",
    path: path.resolve(__dirname, "../dist")
  },
});

  • 最后在package.json 的script 指令中加入指定不同的文件
"scripts": {
    "start": "webpack-dev-server --open --config webpack/webpack.dev.js",
    "build": "webpack --config webpack/webpack.prod.js"
  }

代码分离

代码分离是指能够把不同的模块打包到不同的bundle中,起到减小文件体积,同时能够控制资源加载优先级,起到优化加载的作用。
有3种代码分离的方法,第一种上面讲过entry 可以定义不同文件,第二种方法是利用插件plitChunksPlugin 把重复的模块提取到单独的文件,放置重复在不同页面使用。第三种是动态加载模块,没用过这里就不讲了,下面主要讲第二种方法
在配置中加入 splitChunks

const path = require('path');

  module.exports = {
    mode: 'development',
    entry: {
       app: "./src/index.js",
       print: './src/print.js'
    },
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist'),
    },
  optimization: {
     splitChunks: {
       chunks: 'all',
     },
   },
  };

测试: 例如在index.js和print.js里面都引用了lodash,最后webpack打包会把lodash单独在一个文件中。
在这里插入图片描述
其中最后一个文件就是loadash ,可以看到app.js打包都文件大小较少了很多。
下面测试下不提取公共包,webpack后都文件大小
在这里插入图片描述
会发现app.js 和print.js都非常大。

缓存

缓存是浏览器的一项技术,能提交网页记载速度。但是对于修改的文件,缓存会是很头疼的问题,会导致拿不到新资源。下面是利用优化缓存的一些办法:

output

如果我们的文件名不改变的化,会默认用缓存的文件名,这会导致拿不到最新的资源。output可以定义输出的文件名,[chunkhash]可输出一个内容相关的hash 内容改变的化hash也会改变,从而不会有缓存。配置如下

module.exports = {
	...
	entry: {
    	app: "./src/index.js"
  	},
	output: {
		filename: "[name].[contenthash].js",
		path: path.resolve(__dirname,"../dist")
	}
}

webpack后的文件名如下:
在这里插入图片描述
不改变文件内容,再webpack,这个hash也不会改变。
测试下改变内容后,文件名会改变,如下图:
在这里插入图片描述
如此达到了没有修改的文件从缓存中取,加快了网页加载速度,修改了的文件重新获取的效果。

提取模板

对于那些不会变的代码,可以提取出来,直接利用缓存,加快网页加载速度。例如:模版模块,第三方模块。

optimization.runtimeChunk

能够将运行时的代码提取出来,配置如下

module.exports = {
	...
	entry: {
    	app: "./src/index.js"
  	},
	output: {
		filename: "[name].[contenthash].js",
		path: path.resolve(__dirname,"../dist")
	},
	optimization: {
		runtimeChunk: "single"
	}
}

在这里插入图片描述
不管app.js变动,runtime.js都不会改变。

splitChunks cacheGroups 分离第三方模块

第三方模块也是很少变动的模块,把他们分离出来,减轻app.js的大小,能有效利用缓存。
配置如下:

module.exports = {
	...
	entry: {
    	app: "./src/index.js"
  	},
	output: {
		filename: "[name].[contenthash].js",
		path: path.resolve(__dirname,"../dist")
	},
	optimization: {
		runtimeChunk: "single",
		splitChunks: {
	      cacheGroups: {
	        vendor: {
	          test: /[\\/]node_modules[\\/]/,
	          name: "vendors",
	          chunks: "all"
	        }
	      }
    	}
	}
}

webpack的结果如下:
在这里插入图片描述
在这里插入图片描述
vendor这个文件是node_modules里面的第三方插件。
改变app.js文件,runtime和vendor也不会变。从而也利用了缓存。同时app.js也从1010k缩小到12.8k。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值