webpack 4 详解

Webpack4


webpack默认配置

1. npm init -y
2. npm install webpack webpack-cli -D
3. 创建src / index.js 文件
运行webpack打包,使用npx 会在 node_module里面查找webpack模块
3. npx webpack
打包完成后 会生成 dist/main.js 文件 

根目录创建webpack.config.js文件
默认配置 :

// webpack 是基于 nodejs的
// 要使用CommonJS规范导出一个对象

const path = require('path')

module.exports = {
  // webpack 执行入口
  entry: './src/index.js',
  // 输出
  output: {
    // 输出到哪里,必须是绝对路径
    path: path.resolve(__dirname, './dist'),
    filename: 'main.js'
  },
  // 开发模式
  mode: 'development'
}

如果不在根目录创建webpack.config.js文件,默认是如下配置

  • 默认入口模块

    • ./src/index.js
  • 默认输出

    • 名称是main.js
    • 路径是’./dist’
  • webpack默认支持多种模块类型:commonjs esmodule AMD

    • esmodule 和 commonjs
      • import { } from ‘./’
      • const xxx = require(‘xxx’)
    • webpack默认支持js模块和json模块
  • 注意:

    • loader存在执行顺序:自右往左,自下往上

图片 / 字体资源打包

npm install file-loader -D

./dist/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
	<div id="root"></div>
	<script src="main.js"></script>
</body>
</html>

index.js

import pic from './asset/hz.jpg'
// 处理完成后 pic 即为 输出到路径的图片
// images/a4d6987a21c45234dc8a7e43044f33eb.jpg
console.log(pic)
var img = new Image()
img.src = pic
var root = document.querySelector('#root')
root.append(img)

webpack.config.js

module: {
    rules: [
      // 处理图片
      {
        test: /\.(png|jpg|gif)$/,
        use: [
        	{
              loader: 'file-loader',
              options: {
                //! 定义输出名字 具体查看官方文档 placeholders
                name: '[name]_[hash:8].[ext]',
                outputPath: 'images/',
              }
        	}
          ]
      },
      // 处理字体
      {
        test: /\.(woff2|woff|ttf|eot|svg)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name]_[hash:8].[ext]',
              outputPath: 'font/',
            }
          }
        ]
      },
    ]
  }

此时页面就能显示出图片

css / less / …解析

npm install css-loader style-loader less less-loader -D

index.js

import("./asset/css/index.css")
import("./asset/css/a.css")
import("./asset/css/b.less")

webpack.config.js

      // 处理css
      {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader',
            //! 将多个样式文件打包到一个 style 标签里面
            options: { injectType: 'singletonStyleTag' },
          },
          {
            loader: 'css-loader'
          }
        ]
      },
      // 处理less
      {
        test: /\.less$/,
        use: [
          {
            loader: 'style-loader',
            //! 将多个样式文件打包到一个 style 标签里面
            options: { injectType: 'singletonStyleTag' },
          },
          {
            loader: 'css-loader'
          },
          {
            loader: 'less-loader'
          }
        ]
      },

postcss: css兼容性处理

使用 postcss 库 以及 autoprefixer 插件

npm install postcss-loader autoprefixer -D

  • 第一种

webpack.config.js,在css处理 / less处理中 加入post-loader

{
   loader: 'postcss-loader'
}

新建postcss.config.js

module.exports = {
  plugins: [
    require('autoprefixer')({
      // 配置兼容市场份额大于1%的浏览器最近两个版本 
      overrideBrowserslist: ['last 2 versions', '>1%']
    })
  ]
}
  • 第二种

涉及到postcss这个库。所以需要使用postcss-loader,同时还要用到postcss-preset-env插件

postcss-preset-env 包括autoprefixer ,所以在使用preset时候不需要使用autoprefixer

npm i postcss-loader postcss-preset-env
// webpack.config.js
{
	loader: 'postcss-loader',
		options: {
			ident: 'postcss',
			plugins: [require('postcss-preset-env')()],
		},
},

此外还需要在 package.json 中加入浏览器列表配置(推荐写在 package.json 中),除外还有很多种配置写法。

"browserslist": {
	"development": [
		"last 10 version"
	],
	"production": [
		">0.01%",
		"not dead",
		"not op_mini all"
	]
}

browserslist检测网站

plugins

plugin可以在webpack运行到某个阶段的时候,帮你做一些事情,类似于生命周期的概念

配置html

使用插件:HtmlWebpackPlugin

npm install --save-dev html-webpack-plugin
HtmlWebpackPlugin简化了HTML文件的创建,以便为你的webpack包提供服务。这对于在文件名中包含每次会随着编译而发生变化哈希的 webpack bundle 尤其有用。 你可以让插件为你生成一个HTML文件,使用lodash模板提供你自己的模板,或使用你自己的loader

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
    new HtmlWebpackPlugin({
      title: 'my app',
      template: './src/index.html',
      filename: 'index.html'
    })
  ]

创建html文件作为模板 ./src/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%=htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

重新构建到dist

使用插件:cleanWebpackPlugin

作用:在每次进行构建的时候 将dist删除重新构建新的dist文件
npm install --save-dev clean-webpack-plugin

webpack.config.js

const {CleanWebpackPugin} = require('clean-webpack-plugin')
plugins:[
   new CleanWebpackPugin()
]

提取 css 成单独文件

使用插件:mini-css-extract-plugin

mini-css-extract-pugin

npm install --save-dev mini-css-extract-plugin

将多个css样式文件提取成一个独立文件

webpack.config.js

const miniCssExtractPlugin = require('mini-css-extract-plugin')
module: {
    rules: [
    	{
          test: /\.less$/,
          use: [
            //! 将多个样式文件打包到一个 style 标签里面
            // {
            //   loader: 'style-loader',

            //   options: { injectType: 'singletonStyleTag' },
            // },
            //! 将样式文件提取成一个独立文件
            {
              loader: miniCssExtractPlugin.loader,
              options: {
              }
            },
            'css-loader',
            'postcss-loader',
            'less-loader'
          ]
		},
	]
},	
plugins:[
  new miniCssExtractPlugin({
      filename: '[name]_[chunkhash:8].css',
  })
]

压缩css

使用插件:optimize-css-assets-webpack-plugin

// new一个对象即可,默认配置足够压缩css代码了
new OptimizeCssAssetsWebpackPlugin();

压缩 html 和 js

压缩 js: 只需要将 mode 改成production就可以了 压缩 html: 使用HtmlWebpackPlugin这款插件

plugin: {
	new HtmlWebpackPlugin({
		template: './src/index.html',
		minify: {
			// 移除空格
			collapseWhitespace: true,
			// 移除注释
			removeComments: true,
		},
	});
}

devServer

webpackDevServer

  • 提升开发效率的利器
    每次改完代码都需要重新打包一次,打开浏览器刷新,很麻烦。可以改善这块的体验
  • 安装
    npm install webpack-dev-server -D
  • 配置
    1.在package.json配置
"scripts": {
 	"server": "webpack-dev-server"
},

2.在webpack.config.js中配置

devServer: {
    contentBase: "./dist",
    port: 3000,
    open: true,
    // 设置 proxy 代理 处理跨域
	proxy: {
      '/api': {
        target: 'http://localhost:3030'
        // 当前想访问的接口是localhost:3030/api/info
        // 配置之后访问 '/api/info'
      }
    }
},
  • 启动

npm run server

启动服务后,会发现dist目录没有了,这是因为devServer把打包后的模块不不 会放在dist目录下,而是放到内存中,从而提升速度

  • 本地mock,解决跨域:联调期间,前后端分离,直接获取数据会跨域,上线后我们使nginx转发,开发期间,webpack就可以搞定这件事
devServer:{
	proxy: {
      '/api': {
        target: 'http://localhost:3030'
        // 当前想访问的接口是localhost:3030/api/info
        // 配置之后访问 '/api/info'
      }
    }
}

HMR

  • 第一个版本

热替换模块(Hot Module Replacement))

作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块) 极大提升构建速度。
官方提醒 : 永远不要在生产环境(production)下启用 HMR

注意启动HMR后,css抽离会不生效,还有不支持contenthash, chunkhash

HMR支持style-loader css处理方式,不支持抽离成独立文件(minicssextractplugin)的方式 ,因此css在 style -loader 的作用下 做如下配置即可。

webpack.config.js

const webpack = require('webpack')

devServer: {
    contentBase: "./dist",
    port: 3000,
    open: true,
    //! 即使HMR不生效 浏览器也不会自动刷新,就开启hotOnly:true
    hotOnly: true,
    proxy: {
      '/api': {
        target: 'http://localhost:3030'
        // 当前想访问的接口是localhost:3030/api/info
        // 配置之后访问 '/api/info'
      }
    }
  },
 plugins: [
 	new webpack.HotModuleReplacementPlugin()
 ]

index.js

import('./asset/css/index.css')

var btn = document.createElement('button')
btn.innerHTML = '新增'

document.body.appendChild(btn)
btn.onclick = function () {
  var div = document.createElement('div')
  div.innerHTML = 'item'

  document.body.appendChild(div)
}

index.css

div:nth-of-type(odd) {
  background: red;
}

点击按钮生成隔行变色的div后,改变样式,div不会消失,而样式发生了改变,说明没有刷新页面,只是进行了模块的更新替换

  • 第二个版本

开启热更新:

devServer: {
		contentBase: resolve(__dirname, 'build'),
		open: true,
		port: 9090,
		// 开启HMR功能
		hot: true,
		compress: true,
	},

关于js模块的 HMR**

​ 需要使用module.hot.accept来观察模块更更新 从而更新。手动监听所需要HMR的模块,当它的内容发生改变触发回调函数进行监听

add.js

function add () {
  var op = document.createElement('p')
  op.setAttribute('id', 'op')
  op.innerHTML = 66
  document.body.appendChild(op)
}

export default add

index.js

import('./asset/css/index.css')

var btn = document.createElement('button')

btn.innerHTML = '新增'

document.body.appendChild(btn)
btn.onclick = function () {
  var div = document.createElement('div')
  div.innerHTML = 'item'

  document.body.appendChild(div)
}

// js模块 HMR  需要手动监听需要HMR的模块,当它的内容发生改变触发回调
import add from './add.js'
add()
if (module.hot) {
  module.hot.accept('./add.js', () => {
    console.log('add.js文件发生了改变')
    document.body.removeChild(document.getElementById('op'))
    add()
  })
}

点击按钮新增div之后,改变add.js中的数字,页面的div没有消失,而p标签的数字发生了改变,说明页面没有进行刷新,而是进行了js模块的更新替换

html 文件: 默认不能使用 HMR 功能.同时会导致问题:html 文件不能热更新了~ (不用做 HMR 功能)

解决:修改 entry 入口,将 html 文件引入

module.exports = {
  entry: ['./src/index.js','./src/index.html']
}

关于vue / react 等框架的HMR,官方给出了如下解决方案

​ 社区还有许多其他 loader 和示例,可以使 HMR 与各种框架和库(library)平滑地进行交互……

  • React Hot Loader:实时调整 react 组件。
  • Vue Loader:此 loader 支持用于 vue 组件的 HMR,提供开箱即用体验。
  • Elm Hot Loader:支持用于 Elm 程序语言的 HMR。
  • Redux HMR:无需 loader 或插件!只需对 main store 文件进行简单的修改。
  • Angular HMR:没有必要使用 loader!只需对主要的 NgModule 文件进行简单的修改,由 HMR API 完全控制。

JS兼容性处理(Babel)

官方网站:https://babeljs.io/

中文网站:https://www.babeljs.cn/

Babel是javaScript编译器,能将ES6代码转换成ES5代码,让我们开发过程中可以使用js新特性而不用担心兼容性的问题,还可以通过插件的机制根据需求灵活扩展

Babel在执行编译的过程中,会从项目下的.babelrcJSON文件中读取配置,没有该文件会从loader的options读取配置

babel 处理过程 -> 分析依赖 -> AST(抽象语法树) -> 通过语法转换规则转换代码 -> 生成代码

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

1.babel-loader是webpack 与 babel的通信桥梁,不做把es6转成 es5的工作,这部分工作需要用到@babel/preset-env来做

2.@babel/preset-env里里包含了es,6,7,8转es5的转换规则

默认配置

// webpack.config.js
{
  test: /\.js$/i,
  exclude: /node_modules/i,
  loader: 'babel-loader',
  options: {
 	 // todo something..
  },
},

这样做不会对高级的语法进行转换。

{
   test: /\.js$/,
   exclude: /node_modules/,
   use: [	
   	{
		loader: "babel-loader",
		options: {
			presets: ["@babel/preset-env"]
		}
     }
   ]
}

通过上面的几步还不够,默认的Babel只支持let等一些基础的特性转换,Promise等还没有转换过来,这时候需要借助@babel/polyfill,把es的新特性都装进来,来弥补低版本浏览器中缺失的特性

npm install --save @babel/polyfill

兼容性在 IE9+

index.js 顶部
import '@babel/polyfill'

按需加载,减少冗余

兼容性按需加载 core-js

npm i core-js -D

会发现打包的体积大了很多,这是因为polyfill默认会把所有特性注入进来,假如我想我用到的es6+,才会注入,没用到的不注入,从而减少打包的体积,可不可以呢

当然可以

修改webpack.config.js

options: {
   presets: [
     [
       "@babel/preset-env",
       {
         targets: {
           edge: "17",
           firefox: "60",
           chrome: "67",
           safari: "11.1"
         },
         corejs: 2,//新版本需要指定核⼼库版本
         useBuiltIns: "usage"//按需注入 不用在入口文件import
       }
     ]
   ]
 }

useBuiltIns 选项是 babel 7 的新功能,这个选项告诉 babel 如何配置@babel/polyfill。 它有三个参数可以使用: ①entry: 需要在 webpack 的入口文件import “@babel/polyfill"一次。 babel会根据你的使用情况导入垫片,没有使用的功能不会被导入相应的垫片。②usage: 不需要import,全自动检测,但是要安装@babel/polyfill。(试验阶段) ③false: 如果你import”@babel/polyfill",它不会排除掉没有使用的垫片,程序体积会庞大。(不推荐)

扩展:

babelrc文件:新建.babelrc文件,把options的部分转移到该文件中,就可以了

// .babelrc
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1",
        },
        "useBuiltIns": "usage",
        "corejs": 2 //新版本需要指定核⼼库版本
      }
    ]
  ]
}
// webpack.config.js
{
  test: /\.js$/,
  //! 排除
  exclude: /node_modules/,
  loader: 'babel-loader'
}

以上的配置在写组件库或类库代码时失效,因为@babel/polyfill是全局注入语法的,污染了全局环境

react打包环境

安装依赖

npm install react react-dom -S

npm install @babel/preset-react -D

// 在入口文件'./src/index.js'编写react代码
import React, { Component } from 'react';
import ReactDom from 'react-dom'
class App extends Component {
  render () {
    return (
      <div>
        hello world
      </div>
    );
  }
}
ReactDom.render(<App></App>, document.querySelector('#root'))

多页面打包通用方案

entry:{
 index:"./src/index",
 list:"./src/list",
 detail:"./src/detail"
},
output: {
  filename: '[name].js'
},
new htmlWebpackPlugins({
 title: "index.html",
 template: path.join(__dirname, "./src/index/index.html"),
 filename:"index.html",
 chunks:[index]
})
  • 目录结构调整

    • src
      • index
        • index.js
        • index.html
      • list
        • index.js
        • index.html
      • title
        • index.js
        • index.html
  • 使用 glob.symc 第三方库来匹配路径

    npm install glob html-webpack-plugin -D
    
// webpack.config.js
// MPA 多页面打包通用方案
const path = require('path')
const HtmlWebpackPlugin =require('html-webpack-plugin')
const glob = require('glob')


const setMPA = () => {
   const entry = {};
   const htmlWebpackPlugins = [];
   const entryFiles = glob.sync(path.join(__dirname, "./src/*/index.js"));
   entryFiles.map((item, index) => {
     const entryFile = entryFiles[index];
     const match = entryFile.match(/src\/(.*)\/index\.js$/);
     const pageName = match && match[1];
     entry[pageName] = entryFile;
     htmlWebpackPlugins.push(
         new HtmlWebpackPlugin({
           title: pageName,
           template: path.join(__dirname,
          `src/${pageName}/index.html`),
           filename: `${pageName}.html`,
           chunks: [pageName],
           inject: true
      	 })
     );
  });
 return {
   entry,
   htmlWebpackPlugins
 };
};

const { entry, htmlWebpackPlugins } = setMPA()

module.exports = {
 entry,
 output:{
   path: path.resolve(__dirname, "./build"),
   // filename: "[name].js"
   filename: '[name]_[chunkhash:8].js'
 }
 plugins: [
   // ...
   ...htmlWebpackPlugins//展开数组
 ],
 mode: 'development'
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值