webpack配置及优化
https://github.com/Yanlin-Zhu/webpack
webpack安装
安装本地webpack
安装webpack和webpack-cli
//安装前先npm初始化
npm init -y
npm i webpack webpack-cli -D
webpack可以进行0配置(默认只支持打包js文件)
- 打包工具 - 输出后的结果(js模块)
- 打包 - 支持js模块化
运行npx webpack会进入node_modules/bin/webpack.cmd文件执行
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\webpack\bin\webpack.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\webpack\bin\webpack.js" %*
)
判断是否有node.exe没有则执行上一级webpack文件中的\bin\webpack.js文件
手动配置webpack
- 默认配置文件名字webpack.config.js
// webpack是node写出来的 node的写法
let path = require('path')
module.exports = {
mode: 'development', // 模式默认有两种production development开发模式代码不压缩看的清晰
entry: './src/index.js', // 入口文件
output: {
filename: '/js/bundle.[hash].js', // 打包后文件名
path: path.resolve(__dirname, 'dist'), //打包后路径必须是绝对路径resolve方法把相对路径解析成绝对路径,__dirname加不加都可以,它代表在当前目录下产生一个dist目录
publicPath: 'http://www.weilongyun.com' // 给所有打包文件引入时加前缀,包括css,js,img,如果只想处理图片可以单独在url-loader配置中加publicPath(上传七牛云等cdn加速时可用)
},
}
三种hash值区别如下
https://www.cnblogs.com/giggle/p/9583940.html
打包后文件分析(可略过)
(function(modules) { // webpackBootstrap
// The module cache 先定义一个缓存
var installedModules = {};
// The require function 配置实现了require函数
function __webpack_require__(moduleId) { // 参数"./src/index.js"
// Check if module is in cache 模块是否在缓存中
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// Execute the module function 执行传入this指向,模块,模块的空对象exports: {},require方法
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// Flag the module as loaded
module.l = true;
// Return the exports of the module
return module.exports;
}
// expose the modules object (__webpack_modules__)
__webpack_require__.m = modules;
// expose the module cache
__webpack_require__.c = installedModules;
// define getter function for harmony exports
__webpack_require__.d = function(exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, { enumerable: true, get: getter });
}
};
// define __esModule on exports
__webpack_require__.r = function(exports) {
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
// create a fake namespace object
// mode & 1: value is a module id, require it
// mode & 2: merge all properties of value into the ns
// mode & 4: return value when already ns object
// mode & 8|1: behave like require
__webpack_require__.t = function(value, mode) {
if(mode & 1) value = __webpack_require__(value);
if(mode & 8) return value;
if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
var ns = Object.create(null);
__webpack_require__.r(ns);
Object.defineProperty(ns, 'default', { enumerable: true, value: value });
if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
return ns;
};
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function(module) {
var getter = module && module.__esModule ?
function getDefault() { return module['default']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, 'a', getter);
return getter;
};
// Object.prototype.hasOwnProperty.call
__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
// __webpack_public_path__
__webpack_require__.p = "";
// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = "./src/index.js"); // 传入入口模块
})
// 自执行函数传入一个对象即modules
({
"./src/a.js": // key
(function(module, exports) { // value
eval("module.exports = 'a_moudle'\n\n//# sourceURL=webpack:///./src/a.js?");
}),
"./src/index.js":
(function(module, exports, __webpack_require__) {
eval("let str = __webpack_require__(/*! ./a.js */ \"./src/a.js\")\r\n\r\nconsole.log(str)\n\n//# sourceURL=webpack:///./src/index.js?");
})
});
在package.json中配置运行脚本
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js"
},
webpack-dev-server起本地服务
安装
npm i webpack-dev-server -D
devServer: { // 开发服务器的配置(webpack-dev-server)
port: 3000,
progress: true,
contentBase: './dist' // 执行时执行打包后文件,可以不配置
},
html-webpack-plugin(安装插件打包html)
安装
npm i html-webpack-plugin -D
let HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [ // 数组,放着所有webpack插件
new HtmlWebpackPlugin({ // 用于使用模板打包时生成index.html文件,并且在run dev时会将模板文件也打包到内存中
template: './index.html', // 模板文件
filename: 'index.html', // 打包后生成文件
hash: true, // 添加hash值解决缓存问题
minify: { // 对打包的html模板进行压缩
removeAttributeQuotes: true, // 删除属性双引号
collapseWhitespace: true // 折叠空行变成一行
}
})
]
css-loader style-loader(使用loader打包css文件)
安装
npm i css-loader style-loader -D
配置
module: { // 模块
// loader
rules: [ // 规则 loader的特点-希望单一功能
// css-loader-解析css文件包括css文件的引入(@import)语法等
// style-loader-把css文件插入到head标签中
{
test: /\.css/,
// 字符串只用一个loader,多个loader用数组配置
// loader的顺序默认从右向左执行,从下到上执行
// 如果需要传参loader还可以配置成对象
use: ['style-loader', 'css-loader']
}
]
}
解析less文件配置
安装
npm i less-loader -D
配置
// 可处理less文件除此之外还有sass、stylus
// node-sass、sass-loader、stylus-loader
{
test: /\.less$/,
use: [
'style-loader', // 插入style标签
'css-loader', // 解析路径
'less-loader' // 把less转换成css
]
}
在只安装了less-loader、css-loader、style-loader时引入less文件报错Cannot find module 'less',此时安装less就好了
npm i less -D
mini-css-extract-plugin(抽离css插件)
安装
npm i mini-css-extract-plugin -D
配置
let MiniCssExtractPlugin = require('mini-css-extract-plugin')
new MiniCssExtractPlugin({ // 抽离css样式
filename: '/css/main.css'// 抽离出来的文件名
})
{
test: /\.css$/,
// 字符串只用一个loader,多个loader用数组配置
// loader的顺序默认从右向左执行,从下到上执行
// 如果需要传参loader还可以配置成对象
use: [
// 'style-loader',
MiniCssExtractPlugin.loader, // 将css文件抽离出来,不再以style标签存放,而是创建link标签引入
'css-loader'
]
},
// 可处理less文件除此之外还有sass、stylus
// node-sass、sass-loader、stylus-loader
{
test: /\.less$/,
use: [
// 'style-loader', // 插入style标签
MiniCssExtractPlugin.loader, // 将less文件抽离出来,不再以style标签存放,而是创建link标签引入
'css-loader', // 解析路径
'less-loader' // 把less转换成css
]
}
使用mini-css-extract-plugin抽离css插件文件时可使用optimize-css-assets-webpack-plugin优化压缩css以及js文件
optimize-css-assets-webpack-plugin
使用optimize-css-assets-webpack-plugin压缩css文件就必须使用uglifyjs-webpack-plugin压缩js文件
虽然webpack 5可能带有一个内置的CSS最小化器,但对于webpack 4,您需要自带一个CSS最小化器。为了缩小输出,可以使用像optimize-css-assets-webpack-plugin这样的插件。设置优化。最小化器覆盖webpack提供的默认值,所以请确保还指定了JS最小化器:
安装
npm i optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin -D
配置
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
optimization: { // 优化项
minimizer: [
new UglifyJsPlugin({ // 压缩js
cache: true,
parallel: true,
sourceMap: true // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({}) // 压缩css
]
},
autoprefixer包(自动给css样式添前缀)该包需要通过postcss-loader处理
安装
npm i postcss-loader autoprefixer -D
webpack.config.js中配置
{
test: /\.css$/,
// 字符串只用一个loader,多个loader用数组配置
// loader的顺序默认从右向左执行,从下到上执行
// 如果需要传参loader还可以配置成对象
use: [
// 'style-loader',
MiniCssExtractPlugin.loader, // 将css文件抽离出来,不再以style标签存放,而是创建link标签引入
'css-loader',
'postcss-loader'
]
},
// 可处理less文件除此之外还有sass、stylus
// node-sass、sass-loader、stylus-loader
{
test: /\.less$/,
use: [
// 'style-loader', // 插入style标签
MiniCssExtractPlugin.loader, // 将less文件抽离出来,不再以style标签存放,而是创建link标签引入
'css-loader', // 解析路径
'postcss-loader',
'less-loader' // 把less转换成css
]
}
新建postcss.config.js配置文件
module.exports = {
plugins: [require('autoprefixer')]
}
js处理
使用babel将ES6或更高级的语法转换成ES5
安装
npm i babel-loader @babel/core @babel/preset-env -D
配置
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: { // 用babel-loader吧es6转换成es5
presets: [ // 预设规则
'@babel/preset-env'
]
}
}
]
}
未使用babel前使用es6语法会报错
ERROR in bundle.d92a2720a0d7daf51415.js from UglifyJs
Unexpected token: name (str) [./src/index.js:1,0][bundle.d92a2720a0d7daf51415.js:91,4]
此时写类和装饰器(es7)还是会报错
ERROR in ./src/index.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: D:\zhuyanlin\webpack\src\index.js: Support for the experimental syntax 'classProperties' isn't currently enabled (8:5):
安装
npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
配置
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: { // 用babel-loader吧es6转换成es5
presets: [ // 预设规则
'@babel/preset-env'
],
plugins: [ // 此处配置有顺序
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
}
}
]
}
如果您手动包含插件并使用@babel/plugin-proposal-class属性,请确保@babel/plugin-proposal-class装饰器位于@babel/plugin-proposal-class属性之前。
npm i @babel/plugin-transform-runtime -D
npm i @babel/runtime -S
plugins: [ // 此处配置有顺序
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }],
"@babel/plugin-transform-runtime"
]
全局变量的引入(jQuery、Lodash...)
安装
npm i jquery -S
配置
let webpack = require('webpack')
new webpack.ProvidePlugin({ // 在每个模块中都注入$
$: 'jquery'
})
当使用cdn在模板中引入时<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>可通过配置
externals: {
jquery: "$"
},
此时可以通过$或window.$调用,再在文件中写import $ from 'jquery'时依然采用外部引入jQuery就不会被打包,打包文件不会增大。
打包图片
- 在js中创建图片引入
import lion from './static/img/img/lion' // 把图片引入返回一个新的图片地址
let image = new Image()
image.src = lion
document.body.appendChild(image)
- 在css背景图片中引入
background: url('./static/img/img/ABC.png')
- 在html中通过标签引入
file-loader(适用于前两种情况引入图片,貌似可以不用也没报错)
file-loader默认会生成一张图片到dist目录下,把生成图片的名字返回回来
安装
npm i file-loader -D
配置
{
test: /\.(png|jpg|gif)$/,
use: 'file-loader'
}
html-withimg-loader(适用于html标签引入图片)
安装
npm i html-withimg-loader -D
配置
{
test: /\.html$/,
use: 'html-withimg-loader'
}
url-loader(可取代file-loader包含file-loader的功能)
安装
npm i url-loader -D
配置
{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
// 做一个限制,当图片小于多少k时用base64转化,否则用file-loader将图片产出
options: {
limit: 200*1024,
// dist打包文件中的输出路径
outputPath: '/img/'
}
}
}
打包多页面应用
代码在git上的morepage分支npx webpack运行打包
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].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
filename: 'home.html',
chunks: ['home'] // 模板引入homejs模块
}),
new HtmlWebpackPlugin({
template: './index.html',
filename: 'other.html',
chunks: ['other'] // 模板引入otherjs模块
})
]
}
配置source-map
// 源码映射,打包时会在dist中单独生成一个source-map(.map)文件,出错时会标识打包前文件当前出错的类和行
// devtool: 'source-map', // 增加的映射文件可以帮我们调试源代码,特点:大和全
// devtool: 'eval-source-map', // 不会产生单独的映射文件会直接打包到打包文件中,可以产生报错行和列
// devtool: 'cheap-module-source-map', // 不会产生列,会产生一个单独的映射文件
devtool: 'cheap-module-eval-source-map', // 不会产生文件,也不会产生列
performance: {
hints:false
},
watch的用法,修改代码后实时打包文件
watch: ture,
watchOptions: { // 监控选项
poll: 1000, // 每秒问我一千次
aggregateTimeout: 500, // 防抖,我一直输入代码
ignored: /node_modules/ // 不需要监控的文件
}
webpack的三个插件
1.cleanWebpackPlugin:删除上次打包的文件
安装
npm i clean-webpack-plugin -D
配置
let CleanWebpackPlugin = require("clean-webpack-plugin")
new CleanWebpackPlugin()
2.copyWebpackPlugin:将文档等内容打包进打包文件
安装
npm i copy-webpack-plugin -D
配置
let CopyWebpackPlugin = require("copy-webpack-plugin")
new CopyWebpackPlugin([
{from: './src/doc', to: './'}
])
3.bannerPlugin (内置):打包后文件中版权作者等标注信息
配置
let webpack = require('webpack')
new webpack.BannerPlugin('make 2019 by zhuyanlin')
webpack跨域
配置代理
proxy:{
"/fe": {
target: "http://127.0.0.1:3000",
changeOrigin: true,
ws: true,
pathRewrite: {
"^/fe": ""
}
}
}
resolve
解析顺序别名等配置
https://www.webpackjs.com/configuration/resolve/#resolve
定义环境变量
new webpack.DefinePlugin({
DEV: JSON,stringify('dev'),
AAA: 'true'
})
区分不同环境
将不环境配置文件分开
npm i webpack-merge -D
noParse
不去解析某些包中的依赖关系
IgnorePlugin moment库(时间插件)
npm i moment -S
import moment from 'moment';
console.log(moment().endOf('day').fromNow())
dullPlugin
npm i react react-dom -S
npm i @babel/preset-react -D
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: { // 用babel-loader吧es6转换成es5
presets: [ // 预设规则
'@babel/preset-env',
'@babel/preset-react'
],
plugins: [ // 此处配置有顺序
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }]
// "@babel/plugin-transform-runtime"
]
}
}
],
// exculde: /node_modules/
// inculde: path.resolve(__dirname, 'src')
},
。。。未完待续
webpack生命周期
http://taobaofed.org/blog/2016/09/09/webpack-flow/
image.pngwebpack搭建一个react项目并优化
npm init // 创建package.json
npm i webpack -D
npm i react react-dom -S
npm i @babel/core babel-loader @babel/preset-env @babel/preset-react --save-dev
npm i html-webpack-plugin html-loader --save-dev
npm i webpack-dev-server --save-dev
// webpack.config.json
const path = require('path');
const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = {
entry: path.resolve(__dirname, './src/index.js'), //指定入口文件,程序从这里开始编译,__dirname当前所在目录, ../表示上一级目录, ./同级目录
output: {
path: path.resolve(__dirname, './dist'), // 输出的路径
filename: 'bundle.js' // 打包后文件
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: 'babel-loader',
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
}
},
exclude: /node_modules/
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./index.html",
filename: "./index.html"
})
],
devServer: {
contentBase: path.join(__dirname, "dist"),
compress: true,
port: 9000
}
}
HappyPack
分进程变异
npm i -D happypack
懒加载和预加载,webpack生产环境自带优化,摇树优化和变量提升
抽离公共代码用于打包多入口项目