webpack介绍
安装
npm install webpack webpack-cli --save-dev
loader
enforce 分类
- pre 前置
- normal 正常
- inline 行内也被称为内联
- post 后置
mode
- --mode process.env.NODE_ENV 只是在模块内可用,在node环境中不可用
- --env
node取不到 模块内也取不到
只能在配置文件的参数中拿到 - cross-env 设置环境变量
NODE_ENV 真正的操作系统的环境变量
node里可以取到,其他地方都取不到 配置文件参数 模块内都取不到
windows
- set key=value
- echo %key%
mac - export key=value
打包文件
- 如果文件名不一样,不会直接覆盖,一样的话就会覆盖,想删除老的就需要手动清除
- 不想手动清除 clean-webpack-plugin
- 模块id根源文件的路径有关,其实就是源文件相对于根目录的相对路径,跟打包后的文件没有关系
babel
npm i babel-loader @babel/core @babel/preset-env @babel/preset-react -D
npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
- 默认情况下有些代码webpack是不认识的 比如jsx 有些代码是浏览器不兼容的 es6 es7
- 把es6、es7编译成es5
- 把React编译成es5
- 靠babel-loader
- babel-loader只是一个转换函数,并不能识别JS的语法,也不知道如何转换
- 得认识JS代码,知道如何把新代码转换成老代码
- @babel/core 它是babel的核心模块,它认识JS代码,能够识别JS代码,不知道如何转换写法
- babel插件知道如何把新语法转成老语法,每个插件对应一个语法,比如说箭头函数
- plugin-transform-arrow-functions 可以把箭头函数转成普通函数
- ES6/ES7语法是很多的
- 把插件打包成preset预设,预设就是插件的集合
- es6->es5的所有的插件打成一个包 @babel/preset-env
eslint
webpack调用eslint eslint会读配置文件
airbnb是规则的集合 现在是eslint:recommended
最终都是给eslint来读取规则
npm i eslint eslint-loader babel-eslint -D
LF
line feed 换行
CRLF
carriage return line feed 回车换行
windows 换行符 \r\n \r回车 \n换行
linux里换行符\n 没有\r
来自于老式打印机
sourcemap
- eval 使用eval包裹模块代码 sourcemap信息分开了 eval("var a = 1 //# sourceURL=[module]\n) 使用sourcemap的缓存,可以提升重复构建的速度 如果你生成的是一个单独的map文件,所有的模块的map信息耦合在一起的。 如果一个模块发生变化,整表map文件都要重新计算生成 eval 每个模块的map文件单独存放,可以单独缓存 有一个模块发生变更,只需要重新计算着一个模块的map信息就可以了
- source-map 产生.map文件
- cheap 不包含列信息也不包含loader的sourcemap
- module 包含loader的sourcemap(比如jsx to js,babel的sourcemap),否则无法定义源文件
- inline 将.map文件作为DataURL嵌入,不单独生成.map文件
组合规则
[inline-[hidden-|eval-][nosources-][cheap-[module-]]source-map
- source-map 单独在外部生 成完整的sourcemap文件,并旦在目标文件里建立关联,能提示错误代码的准确原始位置
- inline-source-map 以base64格式内联在打包后的文件中,内联构建速度更快,也能提示错误代码的准确原始位置
- hidden-source-map 会在外部生成sourcemap文件,但是在目标文件里没有建立关联,不能提示错误代码的准确原始位置
- eval-source-map 会为每一个模块生成一个单独的sourcemap文件进行内联,并使用 eval 执行
- rosources-source-map 也会在外部生成sourcemap文件,能找到源始代码位置,但源代码内容为空
- cheap-source-map 外部生成sourcemap文件,不包含列和loader的map
- cheap-module-source-map 外部生成sourcemap文件,不包含列的信息但包含loader的map
最佳实践
开发环境
- 我们在开发环境对sourceMap的要求是:速度快,调试更友好
- 要想谏度快推荐
eval-cheap-source-map - 如果想调试更友好 cheap-module-source-map
- 折中的选择就是 eval-source-map
生产环境
- 首先排除内联,因为一方面我们了隐藏源代码,另一方面要减少文件体积
- 要想调试友好 sourcemap>cheap-source-map/ cheap-module--source-mapshidden-source-map/ nosources-sourcemap
- 要想速度快 优先选择 cheap
- 折中的选择就是 hidden-source-map
调试代码
测试环境调试
- source-map-dev-tool-plugin实现了对 source map 生成,进行更细粒度的控制
- filename (string):定义生成的source map 的名称(如果没有值将会变成 inlined)。
- append (string):在原始资源后追加给定值。通常是 #source MappingURL 注释。(ur] 被替换成 source map 文件的URL
- 市面上流行两种形式的文件指定,分别是以 。 和# 符号开头的,® 开头的已经被废奔
开发服务器
webpack-dev-middleware
webpack-dev-middleware就是在 Express 中提供webpack-dev-server 静态服务能力的一个中间件
npm install webpack-dev-middleware -save-dev
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);
- webpack-dev-server的好处是相对简单,直接安装依赖后执行命令即可
- •而使用
webpack-dev-middleware
的好处是可以在既有的 Express 代码基础上快速添加 webpack-dev-server 的功能,同时利用 Express 来根据需要添加更多的功能,如mock 服务、代理 API 请求等
生产环境
提取css
- 因为CSS的下载和JS可以并行,当一个HTML文件很大的时候,我们可以把CSS单独提取出来加载
安装
- mini-css-extract-plugin
npm install mini-css-extract-plugin --save-dev
基本配置
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const EslintWebpackPlugin = require("eslint-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// const FilemanagerPlugin = require('filemanager-webpack-plugin')
// const webpack = require('webpack')
console.info("🚀 ~ log:process.env.NODE_ENV ----->", process.env.NODE_ENV);
module.exports = {
mode: "development",
// 生成单独的source-map,但是不在main.js里建立关联
devtool: false, // 不生成sourcemap,关掉内部生成sourcemap的逻辑,我要自己精细化控制生成的过程
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"), // 会成为默认的静态文件根目录
filename: "main.js",
publicPath: "/", // 加载产出文件的时候的路径前缀
},
// watch:true, // 默认是false,监听文件的变化
// watchOptions:{
// ignored:/node_modules/, // 忽略监控的文件夹
// aggregateTimeout:300, // 默认300ms,防抖
// poll:1000 // 默认1000ms,轮询
// },
// 内部就是一个express服务器
devServer: {
port: 8080, // 配置http服务预览的端口号,如果不设置默认就是8080
// open:true, // 编译成功后会自动打开浏览器
compress: true, // 是否启动gzip压缩
static: path.resolve(__dirname, "public"), // 额外的静态文件的根目录
// onBeforeSetupMiddleware(devServer){ // express()
// if (!devServer) {
// throw new Error('webpack-dev-server is not defined');
// }
// devServer.app.get('/api/users', (req, res) => {
// res.json([
// {
// id:1,
// name: 'jerry',
// age: 18
// },
// {
// id:2,
// name: 'cherry',
// age: 24
// }
// ])
// })
// }
// proxy:{
// '/api':{
// target:"http://localhost:3000",
// pathRewrite:{
// '^/api':''
// }
// }
// }
},
// 配置外部模块
externals: {
jquery: "jQuery",
lodash: "_",
},
resolve:{
alias:{
"@":path.resolve(__dirname,'public')
}
},
module: {
// use是使用哪些loader进行转换,顺序从右往左
// 最右边的loader接收源文件,最左侧的loader返回一个js脚本
// 为什么不用一个loader干所有的事情,而是用小loader组合起来,单一原则,每个loader只做单一的一件事情
rules: [
// {
// test:/\.js$/,
// loader:'eslint-loader',
// enforce:'pre', // 前置 优先执行
// options:{ fix:true }, // 如果发现有问题自动修复
// exclude:/node_modules/ // 排除node_modules目录
// },
// 他可以把一个变量放在全局对象上
// {
// test:require.resolve('lodash'),
// loader:"expose-loader",
// options:{
// exposes:{
// globalName:'_', // 放的全局变量名
// override:true // 如果原来这个变量名有值,是否覆盖
// }
// }
// },
{
test: /\.js$/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
plugins: [
["@babel/plugin-proposal-decorators", { legacy: true }],
[
"@babel/plugin-proposal-private-property-in-object",
{ loose: true },
],
["@babel/plugin-proposal-private-methods", { loose: true }],
["@babel/plugin-proposal-class-properties", { loose: true }],
],
},
},
],
},
{
test: /\.css$/,
// less-loader less转换成css css-loader 处理import和url的 style-loader把css编程js脚本的
// use:['style-loader','css-loader','postcss-loader'],
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: false,
url:true,
import:true
},
},
"postcss-loader",
],
},
{
// 以.module.css结尾的文件使用css模块化
test: /\.module\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
// modules: true, // 开启css模块化
modules: {
mode: "local",
localIdentName: "[path][name]__[local]--[hash:base64:5]", // 自定义css模块化的类名
},
},
},
"postcss-loader",
],
},
{
test: /\.less$/,
// use:['style-loader','css-loader','postcss-loader','less-loader']
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"less-loader",
],
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"sass-loader",
],
// use:['style-loader','css-loader','postcss-loader','sass-loader']
},
{
test: /\.(jpg|png|gif|bmp|svg)$/,
// use:[
// file-loader可以把src目录里依赖的图片文件拷贝到目标目录里去,文件名一般为新的hash值
// {
// loader:"url-loader",
// options:{
// esModule:false,
// name:`[hash:8].[ext]`,
// limit:1024*8, // 如果文件太小,比如雪碧图,不需要拷贝文件,也不需要发http请求了,只需要把文件编程base64字符串,内嵌到页面中
// outputPath:'images', // 指定输出的目录
// publicPath:'images' // 指定引入的时候的目录
// }
// }
// ],
// type: 'javascript/auto'
// type: "asset/resource", // 替代以前的file-loader
// generator: { // 生成的文件目录
// publicPath: "images/",
// outputPath: "images",
// },
// type: "asset/inline", // 替代以前的url-loader
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 8 * 1024, // 8kb
},
},
},
],
},
plugins: [
new EslintWebpackPlugin({
extensions: ["js", "jsx"],
fix: true,
}),
new HtmlWebpackPlugin({
template: "./index.html",
}),
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: ["**/*"],
}),
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, "public"),
to: path.resolve(__dirname, "dist/public"),
},
],
}),
new MiniCssExtractPlugin({
filename: "css/main.css",
}),
// new webpack.ProvidePlugin({
// _:'lodash'
// })
// 由此插件来控制sourcemap的生成
// new webpack.SourceMapDevToolPlugin({
// // 向输出的文件里添加的映射文本
// append:`\n//# sourceMappingURL=http://127.0.0.1:8081/[url]`,
// filename:`[file].map`, // main.js sourcemap文件名叫 main.js.map
// }),
// 将要发布测试环境
// 生成sourcemap文件,但是sourcemap只放在本机,并不部署到测试环境
// new FilemanagerPlugin({
// events:{
// onEnd:{
// copy: [
// { source: './dist/**/*.map', destination: path.resolve(__dirname,"maps") },
// ],
// delete: ['./dist/**/*.map'],
// }
// }
// }),
// new webpack.DefinePlugin({
// "process.env.NODE_ENV":JSON.stringify(process.env.NODE_ENV) // '"development"'
// }),
],
};