Webpack基础配置

1. 引入类

1.1 webpack安装
npm install --save-dev webpack
npm install --save-dev webpack-cli
npm install --save-dev webpack-dev-server
entry: {
    main: './src/index.ts'
},
output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].[contentHash],js',
    chunkFilename: "[id].[contentHash],js"
},
// package.json
"scripts": {
  "dev": "webpack-dev-server --config webpack.common.js",
  "build": "webpack --env.production --config webpack.common.js"
},
1.2. loader

将不同类型的文件进行不同形式的处理

1.2.1 CSS相关

mini-css-extract-plugin将样式文件单独打包成一个css文件
style-loader将样式通过<style>注入页面
css-loader解析打包css文件
sass-loader解析打包sass文件
autoprefixerpostcss-loader的插件,为样式添加前缀兼容浏览器

// 二选一
npm install --save-dev mini-css-extract-plugin // 生产环境
npm install --save-dev style-loader // 开发环境

npm install --save-dev css-loader
npm install --save-dev sass-loader

npm install --save-dev postcss-loader
npm install --save-dev autoprefixer
module: {
      rules: [{
           test: /\.scss$/,
           use: [
               { loader: MiniCssExtractPlugin.loader },
               { loader: 'css-loader', options: { importLoaders: 2 }},
               'sass-loader',
               {
                   loader: 'postcss-loader',
                   options: {
                       ident: 'postcss',
                       plugins: [
                           require('autoprefixer')
                       ]
                   }
               }
           ]
      }]
  },
plugins: [
	new MiniCssExtractPlugin({
        filename: '[name].[contentHash].css', // 修改直接引入的CSS文件名
        chunkFilename: '[id].[contentHash].css', // 间接(css中@import)引入的文件名
    })
]

兼容浏览器需要在package.json中添加兼容的版本号

"browserslist": [
  "iOS >= 6",
  "Android >= 4",
  "IE >= 9"
]
1.2.2 JS相关

typescript ts-loader使用ts并解析ts文件

注:ts配置文件需要同时配置include和exclude,如果只配exclude,ts还是会检查exclude文件夹。如使用html-webpack-plugin插件且webpack入口文件是ts文件时,打包会报错,报错显示html-webpack-plugin插件内有不满足ts的语法

@babel/core @babel/cli安装babel
@babel/preset-env进行JS语法转换
@babel/polyfill进行JS代码垫片注入

npm install --save-dev typescript ts-loader
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill

配置.babelrc文件

// .babelrc
{ 
    "presets": [["@babel/preset-env", {
      "targets": {
        "edge": "17",
        "firefox": "60",
        "chrome": "67",
        "safari": "11.1", 
      },
      "useBuiltIns": "usage" // 会自动注入polyfill无需手动注入
    }]]
}
{
    test: /\.js$/,
    exclude: /node_modules/,
    loader: "babel-loader"
},
{
    test: /\.tsx?$/,
    exclude: /node_modules/,
    use: [
        "babel-loader",
        'ts-loader'
    ],
},
1.2.3 文件相关

都用于打包文件,区别在于url-loader可以将大小低于限定值的文件转成base64

npm install --save-dev url-loader
npm install --save-dev file-loader
{
    test: /\.(jpg|png|gif)$/,
    use: {
        loader: "url-loader",
        options: {
            name: '[path][name].[ext]?[hash]',
            outputPath: 'images/',
            limit: 204800
        }
    }
}
{
    test: /\.(woff|woff2|eot|ttf|otf)$/,
    use: [
        'file-loader'
    ]
}
1.3 插件
1.3.1 dist目录

html-webpack-plugin在打包完成后为dist目录生成index.html文件
clean-webpack-plugin在打包前删除dist目录中的文件

两个插件会自动匹配webpack的output配置

npm install --save-dev html-webpack-plugin
npm install --save-dev clean-webpack-plugin
plugins: [
	new CleanWebpackPlugin(), 
	new HtmlWebpackPlugin({
	    template: path.resolve(__dirname, 'src/index.html')
	})
],
1.3.2 打包分析

webpack-bundle-analyzer一种打包分析插件

npm install --save-dev webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
plugins: [new BundleAnalyzerPlugin()]
1.3.3 打包界面优化

FriendlyErrorsWebpackPlugin优化打包界面,使控制台看起来舒服

npm install --save-dev friendly-errors-webpack-plugin

引入同上

1.3.4 打包性能优化

原理:将第三方模块提前打包,在项目中引入,以后打包便不再分析打包第三方模块,从而提高打包性能。

add-asset-html-webpack-pluginhtml-webpack-plugin插件导出的index.html文件中引入其他的js文件
webpack.DllPlugin通过暴露的全局变量,生成manifest映射文件
webpack.DllReferencePlugin将已打包好的代码与第三方包进行映射。在项目中使用第三方模块的方式不变,webpack会自动将包映射到已打包好的代码中获取资源

npm install --save-dev add-asset-html-webpack-plugin
  1. 配置webpack.dll.jspackage.json
// webpack.dll.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
	mode: "production",
	entry: {
		lodash: ['lodash'],
		moment: ['moment']
	},
	output: {
		filename: "[name].dll.js",
		path: path.resolve(__dirname, '../dll'),
		library: '[name]' // 通过全局变量形式暴露代码
	},
	plugins: [
		new webpack.DllPlugin({ 
			name: '[name]', // 对暴露的全局变量进行分析,生成manifest映射文件
			path: path.resolve(__dirname, '../dll/[name].manifest.json')
		})
	]
}
// package.json
"scripts": {
  "build:dll": "webpack --config ./build/webpack.dll.js"
},
  1. 引入插件并配置
// webpack.common.js
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
const files = fs.readdirSync(path.resolve(__dirname, '../dll'))
files.forEach((file) => { // 循环将插件导入
    if (/.*\.dll.js/.test(file)) {
        plugins.push(new AddAssetHtmlWebpackPlugin({
              filepath: path.resolve(__dirname, '../dll/', file)
          }))
    }
    if (/.*\.manifest.json/.test(file)) {
        plugins.push(new webpack.DllReferencePlugin({
              manifest: path.resolve(__dirname, '../dll/', file)
          }))
    }
})

2. 集成类

2.1 sourceMap

将打包文件映射到源代码中,便于调试

devtool: "cheap-module-eval-source-map", // 开发环境使用 
devtool: "cheap-module-source-map", // 生产环境使用
devtool: "none",    // 关闭sourceMap
2.2 热更新

修改代码后实时更新页面

devServer: {
    contentBase: './dist', // 运行目录
    open: true, // 在运行打包命令时自动打开浏览器并显示页面
    port: 8081, // 端口配置
}
2.3 tree shaking

不打包代码中未使用的代码

// package.json
"sideEffects": ["*.scss"] // 配置不使用的文件

ts配置了生产环境也无法适用,第三方包是否支持也无法控制

mode: 'production' // 生产环境自动使用
2.4 代码分割

将代码打包成多个文件便于并行加载
代码更新后只需更新业务代码

同步代码分割:
SplitChunksPlugin已内置,无需安装

splitChunks: {
   chunks: "all",
   cacheGroups: { // 用于cache
        vendor: {
            test: /[\\/]node_modules[\\/]/, // 在node_modules文件夹下的文件
            name: 'vendors', // 打包后的文件名
            chunks: 'all', // 所有的chunk打包成一个文件
        }
    }
}

异步代码分割:
无需配置,当异步引入时webpack会自动分割
使用场景:懒加载

注:如无需代码转译,可略过如下配置
babel-plugin-dynamic-import-webpack用于代码异步加载后使用babel转译

npm install --save-dev babel-plugin-dynamic-import-webpack
// .babelrc
"plugins": ["babel-plugin-dynamic-import-webpack"]
2.5 懒加载

在需要使用时再加载代码,配合prefetch使用更好

webpackPrefetch浏览器空闲后下载
webpackPreload和其他资源一起下载

document.addEventListener('click', () => {
    import( /* webpackPrefetch: true */ './script/compute.js').then(({ default: fn }) => {
        console.log(fn(1, 2))
    })
})

上面代码没卵用,不知道为什么,还是只有点击后才会加载,网上找遍了没找到解决方法

2.6 Shimming

全局导入某个模块

const webpack = require('webpack')
plugins: [
    new webpack.ProvidePlugin({
        $: 'jquery'
    })
],
2.7 路径

简化项目中路径的使用

resolve: {
    extensions: ['.tsx', '.ts', '.js'], // 从左往右匹配后缀
    alias: {
        '@': path.resolve(__dirname,'src') // 规定符号路径代表的根路径
    }
}

3. 环境类

3.1 环境变量

1.1 webpack安装中,命令--env.production会将evn.production设置为true,即可在环境构建中使用

"scripts": {
  "dev": "webpack-dev-server --config ./build/webpack.common.js",
  "build": "webpack --env.production --config ./build/webpack.common.js"
},
3.2 环境构建

webpack-merge用于将webpack配置项合并

npm install --save-dev webpack-merge

创建三个js文件:
webpack.common.js:公用部分

  • entry配置
  • resolve配置
  • css以外的loader配置
  • dist目录配置
  • Shimming配置
  • 代码分割配置
  • 导出配置
module.exports = (env) => {
    if (env && env.production) {
        return merge(commonConfig, proConfig)
    } else {
        return merge(commonConfig, devConfig)
    }
}

webpack.dev.js:开发环境

  • mode配置
  • devtool(sourceMap)配置
  • css相关loader配置(style-loader
  • devServer配置(热更新、请求转发)

webpack.prod.js:生产环境

  • mode配置
  • devtool(sourceMap)配置
  • css相关loader配置(MiniCssExtractPlugin
  • 打包分析配置
  • output配置
3.3 请求转发

请求接口时进行转发

devServer: {
	historyApiFallback: true, // 用于解决非锚点路由页面加载
    proxy: {
        '/api': { // 接口前缀
            target: 'http://www.baidu.com', // 目标服务器
            changeOrigin: true, // 修改请求源
            secure: false, // 忽略https
            pathRewrite: {
                'api': '' // 重写路径
            }
        }
    }
}

4. 其他类

4.1 Library

用于打包库代码,方便使用者通过多种方式引用

externals: 'jquery', // 库中使用了jquery,在打包时忽略jquery的打包
output: {
	library: 'library', // 通过<script>引入,将方法挂载到library变量上
    libraryTarget: 'umd' // 通过import 或 require 引入
}
4.2 ESLint

用于规范代码

npx eslint --init
npm install --save-dev eslint-loader
{
    test: /\.js$/,
    exclude: /node_modules/,
    use: ["babel-loader", "eslint-loader"]
}

5. 打包原理

  1. 使用Node读取入口文件,并用babel将代码解析成AST
  2. 解析AST读取依赖,并将文件代码通过babel转译成浏览器可执行代码
  3. 遍历依赖树,提取所有依赖文件的依赖关系与执行代码,形成依赖关系图(就是一个对象)
  4. 生成执行代码并写入文件
const path = require('path')
const fs = require('fs')
const babelParser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const babel = require('@babel/core')

function moduleAnalyzer(module) { // 生成模块对象
	const moduleContent = fs.readFileSync(module, 'utf-8')
	const ast = babelParser.parse(moduleContent, { // 生成ast
		sourceType: 'module'
	})
	let dependencies = {}

	traverse(ast, { // 变量ast,提取该模块所有依赖
		ImportDeclaration({ node }) {
			const dirname = path.dirname(module)
			const filePath = './' + path.join(dirname, node.source.value)
			dependencies[node.source.value] = filePath
		}
	})
	const { code } = babel.transformFromAst(ast, null, { // 转译模块代码,生成执行代码
		presets: ['@babel/preset-env']
	})
	return { // 导出模块对象
		module,
		dependencies,
		code
	}
}

function dependenciesGraphGenerator (entry) { // 生成依赖关系图
	const entryModule = moduleAnalyzer(entry)
	const graph = [entryModule]
	for (let i = 0; i < graph.length; i++) { // 广度优先遍历,提取所有模块对象,形成依赖图对象
		for (let j in graph[i].dependencies) {
			graph.push(moduleAnalyzer(graph[i].dependencies[j]))
		}
	}
	let dependenciesGraph = {}
	graph.forEach(item => { // 对象结构转换,便于后续使用
		dependenciesGraph[item.module] = {
			dependencies: item.dependencies,
			code: item.code
		}
	})
	return dependenciesGraph
}

function codeGenerator (entry) { // 生成执行代码
	let graph = dependenciesGraphGenerator(entry)
	return `
		(function(graph){
			function require(module){
				function localRequire(relativePath) {
					return require(graph[module].dependencies[relativePath])
				}
				const exports = {}
				;(function(require, exports, code){
					eval(code) // code中会包含exports和require,浏览器无法识别,需要polyfill
				})(localRequire, exports, graph[module].code)
				return exports
			}
			require('${entry}')
		})(${JSON.stringify(graph)}) 
	`
}

function fileWrite(entry) { // 执行代码写入
	const code = codeGenerator(entry) 
	fs.writeFile(path.resolve(__dirname, 'main.js'), code, 'utf-8', (err) => {
		if (err) {
			return console.log(err)
		}
		console.log('success')
	})
}

fileWrite('./src/index.js')
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值