十四、ts
- ts-loader幫助我們處理ts文件, 編譯的時(shí)候就可以處理語(yǔ)法錯(cuò)誤。
// 安裝
yarn add ts-loader --dev
// webpack.config.js
entry: './src/index.js' => entry: './src/index.ts'
// webpack.config.js
{
test: /\.ts$/,
use: ['ts-loader']
}
- preset-typescript,支持polyfill填充,進(jìn)行語(yǔ)法轉(zhuǎn)化。但是不能再編譯時(shí)候就判斷語(yǔ)法錯(cuò)誤。
// 安裝
yarn add @babel/preset-typescript --dev
// webpack.config.js
{
test: /\.ts$/,
exclude: /node_modules/,
// use: ['ts-loader']
use: ['babel-loader']
}
// babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
// 默認(rèn)版本2
corejs: 3
}
],
// 識(shí)別ts文件
[ '@babel/preset-typescript' ]
]
}
- 結(jié)合使用
// package.json
"scripts": {
// --noEmit 不生成文件,只是校驗(yàn)
"build": "tsc --noEmit && webpack",
"serve": "webpack serve"
}
十四、環(huán)境
- paths.js, 不能叫path, 會(huì)內(nèi)部重名.
// 整體處理webpack config里面的路徑
// 這里就不使用相對(duì)路徑
const path = require('path')
const appDir = process.cwd()
const resolveApp = (relativePath) => {
return path.resolve(appDir, relativePath)
}
module.exports = resolveApp
- 公共配置部分webpack.common.js
const resolveApp = require('./paths')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { DefinePlugin } = require('webpack')
const { merge } = require('webpack-merge')
// 導(dǎo)入其它的配置
const prodConfig = require('./webpack.prod')
const devConfig = require('./webpack.dev')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
// 定義對(duì)象保存 base 配置信息
const commonConfig = {
// 反而沒有報(bào)錯(cuò)( 相對(duì)路徑, 相對(duì)于content配置路徑)
// 這里content沒有配置,就找的是package.json里面的webpack config的目錄相對(duì)路徑
entry: './src/index.js',
devtool: 'source-map',
resolve: {
// 添加新的擴(kuò)展名
extensions: [".js", ".json", '.ts', '.jsx', '.vue'],
// 添加別名
// import Home from './components/Home'
// =>
// import Home from '@/components/Home'
alias: {
// 使用相對(duì)路徑
'@': resolveApp('./src')
//'@': path.resolve(__dirname, './src')
}
},
output: {
filename: 'js/main.js',
// 輸出到哪兒
// 使用相對(duì)路徑
path: resolveApp('./dist'),
// path: path.join(__dirname, 'dist'),
// 默認(rèn)空字符串, 瀏覽量會(huì)幫我們添加/
// publicPath: ''
// 自己添加/, 但是這樣本地路徑會(huì)找不到
// publicPath: '/'
// 相對(duì)路徑, 本地可以訪問,但是webpack serve會(huì)找不到
// publicPath: './'
// publicPath: '/lg'
// assetModuleFilename: 'img/[name].[hash:4][ext]'
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', {
loader: 'css-loader',
options: {
// 代表向前找一個(gè)loader處理
importLoaders: 1,
esModule: false
}
}, 'postcss-loader']
},
{
test: /\.less$/,
use: ['style-loader', {
loader: 'css-loader',
options: {
// 代表向前找一個(gè)loader處理
importLoaders: 1,
esModule: false
}
}, 'postcss-loader', 'less-loader']
},
// 全部轉(zhuǎn)成base64
// {
// test:/\.(png|svg|gif|jpe?g)$/,
// type: 'asset/inline'
// }
// 使用
{
test:/\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: 'img/[name].[hash:4][ext]'
},
parser: {
dataUrlCondition: {
maxSize: 20 * 1024
}
}
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:3][ext]'
}
},
{
// js或者jsx
test: /\.jsx?$/,
// 防止node_modules中的庫(kù)也使用了babel,導(dǎo)致干擾
exclude: /node_modules/,
use: [ 'babel-loader' ]
},
{
test: /\.vue$/,
exclude: /node_modules/,
use: ['vue-loader']
},
{
test: /\.ts$/,
exclude: /node_modules/,
// use: ['ts-loader']
use: ['babel-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'jerry-webpack-plugin',
template: './public/index.html'
}),
// 定義常量
new DefinePlugin({
// 這里要添加雙引號(hào),DefinePlugin會(huì)把數(shù)據(jù)原封不動(dòng)返回
BASE_URL: '"./"'
}),
// 針對(duì)vue-loader > 15的版本,需要手動(dòng)設(shè)置插件
new VueLoaderPlugin()
]
}
module.exports = (env) => {
const isProduction = env.production
// 設(shè)置線程環(huán)境, 讓config中也可以判斷環(huán)境
process.env.NODE_ENV = isProduction ? 'production' : 'development'
// 依據(jù)當(dāng)前的打包模式來(lái)合并配置
const config = isProduction ? prodConfig : devConfig
const mergeConfig = merge(commonConfig, config)
return mergeConfig
}
- 正式環(huán)境webpack.prod.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
mode: 'production',
plugins: [
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
{
// 省略了to,to默認(rèn)output.path的文件夾
from: 'public',
globOptions: {
// index.html上面已經(jīng)操作過(guò)了,需要排除。
// 默認(rèn)從項(xiàng)目根找, **代表從當(dāng)前目錄找
ignore: ['**/index.html']
}
}
]
})
]
}
- 測(cè)試環(huán)境webpack.dev.js
const resolveApp = require('./paths')
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
module.exports = {
mode: 'development',
devtool: 'cheap-module-source-map',
// 開發(fā)階段屏蔽 .browserslistrc
target: 'web',
// 開啟HMR
devServer: {
// 只熱加載變化的部分
hot: 'only',
// 開啟壓縮
compress: true,
// 處理直接向后端請(qǐng)求,導(dǎo)致404的問題
historyApiFallback: true,
proxy: {
// /info/users
// http://localhost:4000/info/users
// https://api.github.com/info/users
'/info': {
target: 'https://api.github.com',
// 重寫
pathRewrite: { "^/info": "" },
// 修改host
changeOrigin: true
}
},
static: {
// 代表生成的文件在哪兒, 需要output里面publicPath和這里一致
// publicPath: '/lg',
// 我們打包之后的資源如果說(shuō)依賴其它的資源,此時(shí)就告知去哪找。
// 強(qiáng)烈建議使用絕對(duì)路徑
directory: resolveApp('./public'),
// 監(jiān)聽directory
watch: true
}
},
plugins: [
// 負(fù)責(zé)讓把具體要使用的插件和webpack結(jié)合起來(lái)
new ReactRefreshWebpackPlugin()
]
}
- 根據(jù)環(huán)境配置 babel.config.js
const presets = [
[
'@babel/preset-env',
{
// false: 不對(duì)當(dāng)前的JS處理做 polyfill 的填充
// usage: 依據(jù)用戶源代碼當(dāng)中所使用到的新語(yǔ)法進(jìn)行填充
// entry: 依據(jù)我們當(dāng)前篩選出來(lái)的瀏覽器決定填充什么
// 使用entry,需要在使用的地方導(dǎo)入頭文件
// import "core-js/stable";
// import "regenerator-runtime/runtime"
useBuiltIns: 'usage',
// 默認(rèn)版本2
corejs: 3
}
],
// 識(shí)別jsx文件
[ '@babel/preset-react' ],
// 識(shí)別ts文件
[ '@babel/preset-typescript' ]
]
const plugins = []
// 依據(jù)當(dāng)前的打包模式來(lái)決定plugins 的值
const isProduction = process.env.NODE_ENV === 'production'
if (!isProduction) {
plugins.push(['react-refresh/babel'])
}
module.exports = {
presets,
plugins
}
- package.json
"scripts": {
"build: "tsc --noEmit && webpack --config ./config/webpack.common.js --env production",
"serve": "webpack serve --config ./config/webpack.common.js --env development"
}
十五、分模塊打包
可以方便加載的時(shí)候,只是加載當(dāng)前需要的模塊,而不是一起都加入到項(xiàng)目中。
- 多入口
const commonConfig = {
entry: {
main1: './src/main1.js',
main2: './src/main2.js'
},
output: {
// filename: 'js/main.js',
// [name]占位
filename: 'js/[name].build.js'
}
}
- 依賴方式
const commonConfig = {
entry: {
main1: { import: './src/main1.js', dependOn: 'lodash' },
main2: { import: './src/main2.js', dependOn: 'lodash' },
lodash: 'lodash'
// 同時(shí)依賴多個(gè)
// main1: { import: './src/main1.js', dependOn: 'shared' },
// main2: { import: './src/main2.js', dependOn: 'shared' },
// shared: ['lodash', 'jquery']
},
output: {
// filename: 'js/main.js',
// [name]占位
filename: 'js/[name].build.js'
}
}
- chunk模式
// webpack.common.js
// webpack 5 自帶
const TerserPlugin = require("terser-webpack-plugin")
const commonConfig = {
entry: {
index: './src/index.js'
},
optimization: {
minimizer: [
new TerserPlugin({
// 默認(rèn)會(huì)生成一個(gè)注釋文件, 這里去除
extractComments: false,
}),
],
splitChunks: {
// all代表同步和異步加載都分模塊
chunks: 'all'
}
}
}
// 會(huì)生成767.build.js,767是chunkId
- splitChunks, 靜態(tài)拆分
splitChunks: {
// all代表同步和異步加載都分模塊
// initial代表同步
chunks: 'initial', // async initial all
// 體積大于20KB的進(jìn)行拆分, 而且拆分后最小20KB
// 一般minSize和maxSize 的值一樣
minSize: 20000, // 20KB
maxSize: 20000, // 20KB
// minSize 和 maxSize優(yōu)先級(jí) 高于 minChunks。
// 如果要一起使用,就這樣設(shè)置
// minChunks代表最少import 引用了一次
minChunks: 1,
// 處理好了, 先放緩存, 后面處理好了, 再看是否合并
cacheGroups: {
// syVendors 和 default 名字隨便定義
syVendors: {
// node_modules文件夾里面,代表三方庫(kù)
// \/代表不同系統(tǒng)里面的路徑符號(hào)
test: /[\\/]node_modules[\\/]/,
// filename 可以使用占位符
filename: 'js/[id]_vendor.js',
// 優(yōu)先級(jí), 默認(rèn)-10。 數(shù)字越大優(yōu)先級(jí)越高
priority: -10,
},
default: {
// import 2次的才拆分
minChunks: 2,
filename: 'js/syy_[id].js',
priority: -20,
}
}
}
- 動(dòng)態(tài)導(dǎo)入
動(dòng)態(tài)導(dǎo)入的會(huì)進(jìn)行自動(dòng)拆分模塊
// 動(dòng)態(tài)導(dǎo)入
// 魔法注釋, 會(huì)把title作為chunk的名字
import(/*webpackChunkName: "title"*/'./title')
// webpack.common.js
output: {
// 設(shè)置chunk文件輸出名字
chunkFilename: 'js/chunk_[name].js'
},
optimization: {
// natural 按自然數(shù)進(jìn)行編號(hào)排序,如果某個(gè)文件當(dāng)前次不再被依賴那么重新打包時(shí)序號(hào)都會(huì)變
// named 建議開發(fā)環(huán)境使用,名字長(zhǎng)會(huì)影響性能
// deterministic 生產(chǎn)環(huán)境使用
chunkIds: 'deterministic',
minimizer: [
new TerserPlugin({
// 默認(rèn)會(huì)生成一個(gè)注釋文件, 這里去除
extractComments: false,
}),
]
}
- runtime chunk
可以將加載模塊的信息抽取出來(lái), 方便做緩存。
optimization: {
// 將加載模塊的信息抽取出來(lái),生成一個(gè)新的runtime文件
// runtimeChunk: true,
// 同樣的導(dǎo)入信息, 只有一個(gè)runtime
runtimeChunk: 'single'
/*其他*/
}
bundle: 直接部署,入口文件
vendor:三方的庫(kù),node_modules下面
chunk: 依賴代碼塊
runtime:連接信息,方便緩存