webpack基础知识总结

970 阅读10分钟

Webpack

1.webpack介绍

webpack本质上是一个用现代js应用程序的静态模块打包工具。当webpack处理应用程序时,它会在内部构建一个依赖图,此依赖图对应映射到项目所需的每个模块,并生成一个或多个bundle.

1.1.安装

  • webpack核心包
  • webpack-cli命令行工具包
npm install webpack webpack-cli -D

1.2.入口(entry)

  • 入口起点指示webpack应用使用哪个模块来作为构建内部依赖图的开始。进入入口起点后,webpack会找出有哪些模块和库是入口起点依赖的。
  • 默认值是./src/index.js,我们可以通过webpack config中的entry属性来指定一个/多个不同的入口起点。
module.exports={
    entry:'./src/index.js'
}

1.3.出口(output)

  • output属性,告诉webpack在哪里输出它创建的bundle,以及如何命名这些文件。
  • 主要输出文件的默认值是./dist/main.js,其它生成文件默认值放置在./dist文件夹中。



属性描述
filename文件名
path打包后的文件路径
publicPath一般设置为'/',打包后插入src中的路径:publicPath+filename
const path=require('path')
module.exports={
    output:{
        filename:'main.js',
        path:path.resolve(__dirname,'dist')
    }
}

1.4.loader

  • webpack只能理解jsjson文件。
  • loaderwebpack能够去处理其它类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加的依赖图中
module:{
  rules:[
    {test:/\.txt$/,use:'raw-loader'}
  ]
}

1.5.插件(plugins)

  • loader用于转化某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
const HtmlWebpackPlugin=require('html-webpack-plugin');
plugins:[
  new HtmlWebpackPlugin({
    template:'./src/index.html'
  })
]

1.6.模式(mode)

  • 前端开发工作中,一般都会有两套环境
  • 开发环境(development),构建结果用于本地开发调试,不进行代码压缩,打印debug信息,包含sourcemap文件
  • 生产环节(production),代码经过压缩处理,运行不打印debug信息,静态文件不包含sourcemap
1.6.1.环境差异
  • 开发环境(development)
    • 需要生产sourcemap文件
    • 需要打印debug信息
    • 需要live reload或者hot reload的功能
  • 生产环境(production)
    • 可能需要分离css成单独的文件,以便多个页面共享同一个css文件
    • 需要压缩HTML/CSS/JS代码
    • 需要压缩图片
  • 默认值为production
1.6.2.区分环境
  • --mode:用来设置模块内的process.env.NODE_ENV
  • --env:用来设置webpack配置文件的函数参数
  • cross-env:用来设置node环境的process.env.NODE_ENV
  • DefinePlugin:用来设置模块内的全局变量
1.6.2.1.命令行配置1

webpackmode默认为production

webpack serve的默认为development

可以通过process.NODE_ENV获取当前环境变量,无法在webpack配置文件中获取此变量

"scripts": {
    "start":"webpack serve",
    "build":"webpack"
}
  • index.js
console.log(process.env.NODE_ENV);//production | development
  • webpack.config.js
console.log(process.env.NODE_ENV);//undefined
1.6.2.2.命令行配置2
"scripts": {
    "start":"webpack serve --mode=development",
    "build":"webpack  --mode=production"
}
1.6.2.3.命令行配置3
"scripts": {
    "start":"webpack serve --env=development",
    "build":"webpack  --env=production"
}
1.6.2.4.mode配置
module.exports={
  mode:'development'
}
1.6.2.5.DefinePlugin
  • 设置全局变量(不是window),所有模块都能读取到该变量的值
  • 可以在任意模块内通过process.env.NODE_ENV获取当前的环境变量
  • 无法在node环境(webpack配置文件中)获取当前的环境变量
new webpack.DefinePlugin({
    PRODUCTION: JSON.stringify(true),
    VERSION: JSON.stringify('1.1.0'),
    BROWSER_SUPPORTS_HTML5: true,
    TWO: '1+1',
})
  • index.js
//相当于eval()
console.log('PRODUCTION',PRODUCTION);//true
console.log("VERSION",VERSION);//1.1.0
console.log("BROWSER_SUPPORTS_HTML5",BROWSER_SUPPORTS_HTML5);//true
console.log("TWO",TWO);//2
1.6.2.6.cross-env
  • 只能设置node环境下的变量NODE_ENV
"scripts": {
  "build": "cross-env NODE_ENV=development webpack"
}
  • webpack.config.js
console.log('process.env.NODE_ENV',process.env.NODE_ENV);// development

2.开发环境配置

  • devServer会启动一个HTTP开发服务器,把一个文件夹作为静态根目录
  • 为了提高性能,使用的内存文件系统(memory-fs)
  • 默认情况下devServer会读取打包后的路径

2.1.开发服务器

2.1.1.安装服务器
npm install webpack-dev-server -D
2.1.2.参数
属性描述
port指定HTTP服务器的端口号
compress是否启动压缩 gzip
contentBase静态文件根目录
open自动打开浏览器
writeToDisk会把打包后的文件写入硬盘一份
publicPath:http://locahost/8080/assets/可有通过http://localhost:8080/assets/来访问打包后的静态资源
devServer: {
    contentBase: path.resolve(__dirname,'static'),
    compress:true,
    port:8080,
    open: true
}
start:'webpack serve'//5.x
start:'webpack-dev-server'//4.x

2.2.支持css

2.2.1.安装插件

npm install style-loader css-loader -D

webpack.config.js,loader是有顺序的,从右到左

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

src/bg.css

body{
  background:red;
}

src/index.css

@import './bg.css';
body{
  color:blue;
}

src/index.html

import './index.css'

2.3.支持less&sass

2.3.1.安装
npm install less less-loader -D
npm install node-sass sass-loader -D
2.3.2.使用

webpack.config.js

module:{
    rules:[
        {test:/\.css$/i,use:['style-loader','css-loader']},
        {test:/\.less$/i,use:['style-loader','css-loader','less-loader']},
        {test:/\.scss$/i,use:['style-loader','css-loader','sass-loader']},
    ]
}

src/index.html

<div id="less-container">less-container</div>
<div id="sass-container">sass-container</div>

src/index.js

import './less.less';
import './sass.scss';

src/less.less

@color:oranger;
#less-container{
    color: @color;
}

src/sass/scss

$color:green;
#sass-container{
    color: $color;
}

2.4.支持图片

2.4.1.安装
  • file-loader解决css等文件中引入的图片路径问题
  • url-loader图片小于limit的时候会把图片base54编码,大于limit参数的时候还是使用file-loader进行拷贝
npm install file-loader url-loader html-loader -D
2.4.2.使用

webpack.config.js

url-loader是对file-loader的加强

{ test: /\.html$/, loader: 'html-loader' },
{
    test: /\.(jpg|png|bmp|gif|svg)$/, use: [{
        loader: 'url-loader',
        options: {
            esModule: false,//默认是true,在代码中取图片是需要.default
            name: '[hash:10].[ext]',//取hash十位作为图片名称,图片ext是扩展名,f0a12b17c9.png
            limit: 8 * 1024//如果文件的体积小于limit,就会转成base64字符串内嵌到HTML中
        }
    }]
}

index.html,html中使用图片

<img src="./images/logo.png" alt="logo.png">//需要html-loader+url-loader/file-loader

index.js,js中使用图片,需要在配置中将esModule设置为false,否则取值要通过logo.default,使用的是file-loader/url-loader

const logo=require('../static/logo.png')
const img=new Image();
img.src=logo;
document.body.appendChild(img);

index.css,css中的url使用图片,使用的是file-loader/url-loader

#img-container{
    width: 400px;
    height: 103px;
    background: url('../static/logo.png');
}

2.5.JS兼容性处理

Babel其实是一个编译javascript的平台,可以把ES6/ES7,React,JSX转义为ES5

预设是插件的集合

  • babel-loader:作用是调用babel/core

  • @babel/core:babel编译的核心包,将ES6转化为ES6语法树,通过babel/preset将ES6转化为ES5语法树,再通过babel/core将ES5语法树转化为ES5代码

  • @babel/preset-env:默认支持语法转换

  • @babel/preset-react:React插件的babel预设

  • @babel/plugin-proposl-decorators:把类和对象装饰器编译成ES5

  • @babel/plugin-proposal-class-proterties:转译静态类属性以及使用属性初始化语法声明的属性

2.5.1.安装
npm install babel-loader @babel/core @babel/preset-env @babel/preset-react -D
npm install @babel/plugin-proposal-decoreators @babel/plugin-proposal-class-properties -D
2.5.2.使用
{
  test:/\.jsx?$/,
  use:[
    loader:'babel/loader',//调用babel/core
    options:{
    	presets:[
    		'@babel/preset-env',//将ES6转化为ES5语法树
    		'@babel/preset-react'//转化jsx语法
  		],
    	plugins:[//这两个plugins是有顺序的
        ['@babel/plugin-proposal-decorators',{legacy:true}],//转化类和对象
        ['@babel/plugin-proposal-class-properties',{loose:true}]//转化静态类属性
      ]
    }
  ]  
}

解决ES6->ES5

let sum = (a, b) => a + b;

解决React

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(<h1>hello word</h1>,document.getElementById('root'));

解决装饰器

/**
 * 
 * @param {*} target 装饰的目标
 * @param {*} key 装饰的key
 * @param {*} descriptor 装饰的属性描述
 */
function readonly(target,key,descriptor){
    descriptor.writable=false;
}
class Person{
    @readonly PI=3.14;
}
2.5.3.兼容性处理

@babel/preset-env:默认支持语法转换,需要开启useBuiltIns才能转换API和实例方法

  • 最新ES语法:比如,箭头函数
  • 最新ES API:比如,Promise
  • 最新ES 实例方法:比如,String.prototype.includes

webpack.config.js

{
  test:/\.jsx?$/,
  use:[{
    loader:'babel-loader',
    options:{
      presets:[
        ['@babel/preset-env':{
         useBuiltIns:'usage',//加载polyfill
         corejs:{version:3},//指定corejs的版本
         target:{//指定要兼容的浏览器
        	chrome:'60',
        	//...
         }
        }]
      ]
    }
  }]
}

useBuiltIns

  • false,不对polyfill做操作,如果引入@babel/polyfill则无视配置的浏览器,引入所有的polyfill
  • entry,根据配置的浏览器兼容,引入浏览器不兼容的polyfill。需要在入口文件手动添加import @babel/polyfill,会自动根基browserslist替换成浏览器不兼容的所有polyfill;
  • usage,根据配置的浏览器兼容,以及代码中用到的API来进行polyfill,实现按需加载。
2.5.4.polyfill-service
  • polyfill.io:实现自动加载浏览器所需的 polyfills
<script src="https://polyfill.io/v3/polyfill.min.js"></script>
2.5.5.babel-runtime
  • babel为了解决全局空间污染的问题,提供了单独的包babel-runtime用以提供编译模块的工具函数
  • 简单说babel-runtime更像是一种按需加载的实现,比如哪里需要使用Promise,只需在哪里的文件头部import Promise from 'babel-runtime/core-js/promise'就可以
import Promise from 'babel-runtime/core-js/promise';

const p = new Promise((resolve) => {
    resolve('ok');
});

p.then((data) => console.log(data));

重写了Promise方法,而不是污染全局,缺点就是每次都需要引入

2.5.6.@babel/plugin-transform-runtime
  • 启用插件@babel/plugin-transform-runtime后,babel就会使用babel-runtime下的工具函数
  • @babel/plugin-transform-runtime插件能够将这些工具函数的代码转换成require语句,指向为对babel-runtime的引用
  • @babel/plugin-transform-runtime就是可以在我们使用新API时自动import babel-runtime里面的`polyfill
plugins: [
  [
    "@babel/plugin-transform-runtime",
    {
      corejs: 2,//当我们使用 ES6 的静态事件或内置对象时自动引入 babel-runtime/core-js
      helpers: true,//移除内联babel helpers并替换使用babel-runtime/helpers 来替换
      regenerator: true,//是否开启generator函数转换成使用regenerator runtime来避免污染全局域
    },
  ],
  ['@babel/plugin-proposal-decorators', { legacy: true }],
  ['@babel/plugin-proposal-class-properties', { loose: true }],
 ],

最佳实践

babel-runtime适合在组件和类库项目中使用,而babel-polyfill适合在业务项目中使用

2.6.ESLint代码校验

2.6.1.安装
npm install eslint eslint-loader babel-eslint -D
2.6.2.使用

webpack.config.js

{
  test:/\.jsx?$/,
  loader:'eslint-loader',
  enforce:'pre',//强制指定顺序,统一规则下pre->normal->inline->post
  options:{fix:true},//启动自动修复
  include:path.resolve(__dirname,'src'),//只检测src目录下的  
  exclude:/node_modules/, //排除node_modules  
}

.eslintrc.js

module.exports = {
    root: true,
    parser: "babel-eslint",
    //指定解析器选项
    parserOptions: {
        sourceType: "module",
        ecmaVersion: 2015
    },
    //指定脚本的运行环境
    env: {
        browser: true,
    },
    // 启用的规则及其各自的错误级别
    rules: {
        "indent": ['error',4],//缩进风格
        "quotes": "off",//引号类型 
        "no-console": "off",//禁止使用console
    }
}
2.6.3.最佳实践(airbnb)

安装

npm install eslint-config-airbnb eslint-loader eslint eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks and eslint-plugin-jsx-a11y -D

.eslintrc.js

module.exports = {
    parser:"babel-eslint",
    extends:"airbnb",
    rules:{
        "semi":"error",
        "no-console":"off",
        "linebreak-style":"off",
        "eol-last":"off"
        "indent":["error",4]
    },
    env:{
        "browser":true,
        "node":true
    }
}
2.6.4.自动修复
  • 安装vscode的eslint插件
  • 配置自动修复参数

.vscode/settings.json

{
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true
      },
}

2.7.sourcemap

sourcemap是为了解决开发代码与实际运行代码不一致时帮助我们debug到原始开发代码的技术.

devtool: 'hidden-source-map'
2.7.1.配置项
类型含义
source-map原始代码,但是编译速度慢
eval-source-map原始代码,相对于source-map,会缓存,速度快,eval
cheap-module-eval-source-map原始代码(只有行内) 同样道理,但是更高的质量和更低的性能
eval生产代码,每个模块都被eval执行,并且存在sourceUrl,带eval的构建模式能cache
cheap-source-map转换代码,没有列信息,从loaders生存的sourcemap没有被使用
cheap-module-source-map没有行信息,有loader映射
cheap-eval-source-map转化代码,没有列信息,每个模块被eval执行,并且sourcemap作为eval的一个dataUrl
2.7.2.最佳实践

开发环境

开发环境对sourceMap的要求:速度快,调试友好

  • 要想速度快,推荐:eval-cheap-source-map

  • 要想调试更友好,推荐:cheap-module-source-map

  • 折中选择:eval-source-map

生产环境

首先排除内联,因为我们要隐藏源代码,同时也要减少文件体积

  • 要想调试友好:sourcemap>cheap-source-map/cheap-module-source-map>hidden-source-map/nosources-sourcemap
  • 要想速度快,优先选择cheap
  • 折中选择hidden-source-map

2.8.打败第三方类库

  • 直接引入:import
  • 插件引入
  • expose-loader
  • externals
  • Http-webpack-externals-plugin
2.8.1.直接引入

比较麻烦,每次都要引入

import _ from 'lodash';

alert(_.join(['a', 'b', 'c'], '_'));
2.8.2.插件引入

webpack配置ProvidePlugin后,在使用时将不再需要import/require进行引入,直接使用即可;

_函数会自动添加到当前模块的上下文,无需显示声明

new webpack.ProvidePlugin({
     _:'lodash'
})
2.8.3.expose-loader
  • expose-loader:可以把模块添加到全局对象上,在调试的时候比较有用
  • 不需要任何其它插件配合,只需要将下面代码添加到所有loader之前
npm install expose-loader -D
//=>入口文件
require('lodash');

//=>webpack.config.js
  module: {
    rules: [
      {
          test: require.resolve('lodash'),
          loader: 'expose-loader',
          options: {
              exposes: {
                  globalName: '_',
                  override: true,
              },
          },
      }
    ]
  }
2.8.4.externals

如果我们想引入一个库,但是又不想让webpack打包,并且又想不影响我们在程序中以CMD,AMD或者window/global全局等方式进行使用,那就可以通过配置externals

  • index.html
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
  • index.js
require('lodash');
  • webpack.config.js
{
  externals:{
    lodash:'_'
  }
}
2.8.5.html-webpack-externals-plugin
npm install html-webpack-externals-plugin  -D
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
new HtmlWebpackExternalsPlugin({
    externals: [
        {
            module: 'lodash', // 模块名
            entry: "https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.20/lodash.js",
            global: '_', // 全局变量名
        },
    ],
}),

2.9.mode&env&cross-env

  • webpack --mode=development:改变的是mode的值,代码中可以通过process.env.NODE_ENV取到mode的值;
  • webpack --env=development:改变的是webpack.config.js中导出的函数参数env的值;
  • cross-env NODE_ENV=development webpack serve:改变的是webpack.config.jsprocess.env.NODE_ENV的值;
2.9.1.--mode

package.json

"start:mode":"webpack serve --mode=development"

index.js

console.log(process.env.NODE_ENV);//=>development
2.9.2.--env

package.json

"start:env":"webpack serve --env=development"

webpack.config.js

module.exports=(env)=>{
  console.log(env.development);
}
2.9.3.cross-env

package.json

"start:cross":"cross-env NODE_ENV=development webpack serve"

webpack.config.js

console.log(process.env.NODE_ENV);//=>development

2.10.代码调试

2.10.1.测试环境调试
  • source-map-dev-tool-plugin:实现了对source map生成,进行更细粒度的控制
    • filenam:定义生成的source map的名称(如果没有值将会变成inlined)
    • append:在原始资源后追加给定值。通常是#sourceMappingURL注释。[url]被替换成source map文件的URL
  • 市面上流行两种形式的文件定义,#@符号开头的,@开头的已经被废弃
2.10.2.安装
npm install e
2.10.3.使用
const FileManagerPlugin=require('file-manager-plugin');
const webpack=require('webpack');
module.exports={
  plugins:[
    new webpack.SourceMapDevToolPlugin({
      filename:'[file].map',
      append:'\n//# sourceMappingURL=http://localhost:8081/[url]'
    }),
    new FileManagerPlugin({
      events:{
        onEnd:{
          copy:[{
            source:'./dist/*.map',
            destination:'/Users/dufeihu/Documents/html/zhufeng/复习/day22-webpack/sourcemap'//追加在打包后代码的后面用来找映射sourcemap的地址
          }],
          delete:['./dist/*.map']
        }
      }
    })
  ]
}
2.10.4.生产环境测试

webpack打包仍然生成sourceMap,但是将map文件挑出放到本地服务器,将不含有map文件的部署到服务器

2.11.watch

当代码发生修改后,自动重新build

module.exports = {
  watch: true, // 默认是false,需要手动启动
  watchOptions: { // watch属性
  ignored: /node_modules/, // 不监控的文件/文件夹,支持正则
  aggregateTimeout: 300, // 监听到文件变化后300毫秒再执执行,默认值300ms
  poll: 1000, // 轮询次数,没秒1000次,默认值1000/m
  }
}

2.12.拷贝静态文件

copy-webpack-plugin可以拷贝源文件到目标目录

2.12.1.安装
npm install copy-webpack-plugin -D
2.12.2.使用
const CopyWebpackPlugin = require('copy-webpack-plugin');

new CopyWebpackPlugin({
  patterns: [{
    from: path.resolve(__dirname,'static'),//静态资源目录源地址
    to: path.resolve(__dirname,'dist/static'), //目标地址,相对于output的path目录
  }],
}),

2.13.清空dist目录

clean-webpack-plugin可以打包前先清空输出目录

2.13.1.安装
npm install clean-webpack-plugin -D
2.13.2.使用
const CleanWebpackPlugin=require('clean-webpack-plugin');
module.exports={
  output:{
    filename:'[name][hash:10].js',
    path:path.resolve(__dirname,'dist')
  },
  plugins:[
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns:['**/*']
    })
  ]
}

2.14.服务器代理

如果你有单独的后台开发服务器API,并且希望在同源名下发送API请求,那么代理某些URL会很有用

2.14.1.不修改路径

请求到api/users现在会被代理到请求http://localhost:3000/api/users

/**
*	请求地址:http://localhost:8080/api/users
* 代理地址:http://localhost:3000/api/users
*/
devServer: {
  proxy: {
    "/api": 'http://localhost:3000'
  }
}
2.14.2.修改路径
/**
*	请求地址:http://localhost:8080/api/users
* 代理地址:http://localhost:3000/users
*/
devServer: {
  proxy: {
      "/api": {
       target: 'http://localhost:3000',
       pathRewrite:{"^/api":""}        
      }            
  }
}
2.14.3.before after

before在webpack-dev-server静态资源中间件处理之前,可以用于拦截部分请求返回特定内容,或者实现简单的数据mock

devServer: {
  before(app){
    // app是一个express()
    app.get('/api/users', function(req, res) { 
       res.json([{ name: 'lisi', age: 20 }]);
    })
  }
}
2.14.4.webpack-dev-middleware

webpack-dev-middleware就是在express中提供webpack-dev-server静态服务能力的一个中间件

  • webpack-dev-server的好处是相对简单,直接安装依赖后执行命令即可
  • 使用webpack-dev-middleware的好处是可以在既有express代码基础上快速添加webpack-dev-server的功能,同时利用express来根据需要添加更多的功能

安装

npm install webpack-dev-middleware -D

使用

const express = require('express');
const app = express();
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackOptions = require('./webpack.config');
webpackOptions.mode = 'development';
const compiler = webpack(webpackOptions);
app.use(webpackDevMiddleware(compiler, {}));
app.listen(3000);

3.生产环境

3.1.提取css

因为css的下载和JS可以并行,当一个HTML文件很大的时候,我们可以把CSS单独提取出来加载

3.1.1安装
npm install mini-css-extract-plugin -D
3.1.2.使用
  • webpack.config.js
const MiniCSSExtractPlugin=require('mini-css-extract-plugin');

module.exports={
  output:{
    path:path.resolve(__dirname,'dist'),
    filename:'[name].js',
    publicPath:'/'
  },
  module:{
    rules:[
      //替换掉之前的style-loader,用MiniCSSExtractPlugin.loader
      {
        test:/\.css$/,
        use:[MiniCSSExtractPlugin.loader,'css-loader']
      },
      {
        test:/\.less$/,
        use:[MiniCSSExtractPlugin.loader,'css-loader','less-loader']
      },
      {
        test:/\.scss$/,
        use:[MiniCSSExtractPlugin.loader,'css-loader','sass-loader']
      },
    ]
  },
  plugins:[
    new MiniCSSExtractPlugin({
      filename:'[name].css'
    })
  ]
}

3.2.指定图片和CSS目录

3.2.1.webpack.config.js
module.exports={
  module:{
    rulues:[
      {
        test:/\.(jpg|png|gif|bmp|svg)$/,
        use:[{
          loader:'url-loader',
          options:{
            esModule:false,
            name:'[hash:10].[ext]',
            limit:8*1028,
            outputPath:'images',
            publicPath:'/images'
          }
        }]
      }
    ]
  }
  plugins:[
  	new MiniCSSExtractPlugin({
  		filename:'css/[name].css'
		})
  ]
}

3.3.hash,chunkhash和contenthash

  • 文件指纹是打包后输出的文件名和文件后缀
  • hash一般是结合CDN缓存来使用,通过webpack构建之后,生成对应文件名自动带上对应的md5值。如果文件内容改变的话,那么对应文件哈希值也会改变,对应的HTML引用的URL地址也会改变,触发CDN服务器从源服务器上拉取对应数据,进而更新本地缓存。

指纹转位符

占位符名称含义
ext资源后缀名
name文件名
path文件相对路径
folder文件所在的文件夹
hash每次webpack构建时生成一个唯一的hash值
chunkhash根据chunk生成hash值,来源于同一个chunk,则hash值就一样
contenthash根据内容生成hash值,文件内容相同hash值就相同

3.4.css兼容性

  • 为了浏览器的兼容性,有时候我们必须加入-webkit-ms-o-moz这些前缀
    • Trident内核:主要代表为IE浏览器,前缀为-ms
    • Gecko内核:主要代表为Firefox,前缀为-moz
    • Presto内核:主要代表为Opera,前缀为-o
    • Webkit内核:主要代表为Chrome和Safari,前缀为-webkit
  • 伪元素::placeholder可以选择一个表单元素的占位文本,它允许开发者和设计师自定义占位文本的样式
3.4.1.安装
  • postcss-loader:可以使用PostCSS处理CSS
  • postcss-preset-env:把现代的CSS转换成大多数浏览器能理解的
  • PostCSS Preset Env已经包含了autoprefixerbrowsers选项
npm install postcss-loader postcss-preset-env -D
3.4.2.postcss.config.js
const postcssPresetEnv = require('postcss-preset-env');

module.exports = {
    plugins: [postcssPresetEnv()],
};
3.4.3.webpack.config.js
module.exports={
  module:{
    rules:[
      {
        test:/\.css$/,
      use:[MiniCSSExtractPlugin.loader,'css-loader','postcss-loader']
      },
      {
        test:/\.less$/,
      use:[MiniCSSExtractPlugin.loader,'css-loader','postcss-loader','less-loader']
      },
      {
        test:/\.scss$/,
      use:[MiniCSSExtractPlugin.loader,'css-loader','postcss-loader','sass-loader']
      },
    ]
  }
}
3.4.4.index.css
::placeholder{
    color: red;
}
3.4.5.index.html
<input placeholder="请输入"/>
3.4.6.编译后结果
:-moz-placeholder{
    color: red;
}
:-ms-input-placeholder{
    color: red;
}
::placeholder{
    color: red;
}

3.5.压缩JS,CSS和HTML

3.5.1.安装
npm install optimize-css-assets-webpack-plugin terser-webpack-plugin -D
3.5.2.webpack.config.js
const OptimizeCssAssetsWebpackPlugin=require('optimize-css-assets-webpack-plugin');
const TerserWebpackPlugin=require('terser-webpack-plugin');
const HtmlWebpackPlugin=require('html-webpack-plugin');

module.exports={
  optimization:{
    minimize:true,
    minimizer:[
      new TerserPlugin()
    ]
  },
  plugins:[
    new HtmlWebpackPlugin({
      template:'./src/index.html',
      minify:{
        collapseWhitespace:true,
        reomoveComments:true
      }
    }),
    new OptimizeCssAssetWebpackPlugin()
  ]
}

3.6.px自动转成rem

  • lib-flexible+rem,实现移动端自适应
  • px2rem-loader自动将px转换为rem
  • 页面渲染时计算根元素的font-size
3.6.1.安装
npm install px2rem-loader lib-flexible -D
3.6.2.index.html

实现类似lib-flexible库的功能

<script>
	const docEle=document.documentElement;
  function setRemUnit(){
    docEle.style.fontSize=docEle.clientWith/10+'px';
  }
  setRemUnit();
  window.addEventListener('resize',setRemUnit)
</script>
3.6.3.reset.css
*{
  margin:0;
  padding:0;
}
#root{
  width:750px;
  height:750px;
  border:1px solid red;
  box-sizing:border-box;
}
3.6.4.webpack.config.js
module.exports={
  module:{
    rules:[
      {
        test:/\.css$/,
        use:[{
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          {
          	loader:'px2rem-loader',
          	options:{
          		remUnit:75,
          		remPrecesion:8
        		}
        	}
        }]
      }
    ]
  }
}

3.7.多入口打包

const fs = require('fs');

const pagesRoot = path.resolve(__dirname, 'src', 'page');
const dirs = fs.readdirSync('./src/page');
const htmlWebpackPlugins = [];
const entry = dirs.reduce((memo, current) => {
    const name = path.basename(current, '.js');
    // eslint-disable-next-line no-param-reassign
    memo[name] = path.join(pagesRoot, current);
    htmlWebpackPlugins.push(new HtmlWebpackPlugin({
        template: path.resolve(__dirname, './src/index.html'),
        filename: `${name}.html`,
        chunks: [`${name}`],
        minify: {
            collapseInlineTagWhitespace: true,
            removeComments: true,
        },
    }));
    return memo;
}, {});

module.exports{
  entry,
  plugins:[
    ...htmlWebpackPlugins
  ]  
}

3.8.webpack-merge

3.8.1.安装
npm install webpack-merge -D
3.8.2.webpack.base.js
module.exports={
  entry:'./src/index.js',
  output:{
    filename:'[name][hash:10].js',
    path:path.resolve(__dirnanem,'dist')
  }
}
3.8.3.webpack.dev.js
const merge=require('webpack-merge');
const base=require('./webpack.base.js')
module.exports=merge(base,{
  mode:'development'
})
3.8.4.webpack.prod.js
const merge=require('webpack-merge');
const base=require('./webpack.base.js')
module.exports=merge(base,{
  mode:'production'
})

3.9.env

3.9.1.安装
npm install dotenv -D
3.9.2..env
NODE_ENV=development
3.9.3.webpack.config.js
require('dotenv').config();
console.log('process.env.NODE_ENV',process.env.NODE_ENV);//=>development