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只能理解js和json文件。loader让webpack能够去处理其它类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加的依赖图中
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_ENVDefinePlugin:用来设置模块内的全局变量
1.6.2.1.命令行配置1
webpack的mode默认为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
css-loader用来处理@import和url();style-loadercss插入DOM中;
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.jsurl-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.js中process.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
- Trident内核:主要代表为IE浏览器,前缀为
- 伪元素
::placeholder可以选择一个表单元素的占位文本,它允许开发者和设计师自定义占位文本的样式
3.4.1.安装
- postcss-loader:可以使用PostCSS处理CSS
- postcss-preset-env:把现代的CSS转换成大多数浏览器能理解的
- PostCSS Preset Env已经包含了
autoprefixer和browsers选项
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
- optimize-css-assets-webpack-plugin:是一个优化和压缩css资源的插件
- terser-webpack-plugin:是一个优化和压缩JS资源的插件
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