十四、ts
- ts-loader帮助我们处理ts文件, 编译的时候就可以处理语法错误。
// 安装
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填充,进行语法转化。但是不能再编译时候就判断语法错误。
// 安装
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',
// 默认版本2
corejs: 3
}
],
// 识别ts文件
[ '@babel/preset-typescript' ]
]
}
- 结合使用
// package.json
"scripts": {
// --noEmit 不生成文件,只是校验
"build": "tsc --noEmit && webpack",
"serve": "webpack serve"
}
十四、环境
- paths.js, 不能叫path, 会内部重名.
// 整体处理webpack config里面的路径
// 这里就不使用相对路径
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')
// 导入其它的配置
const prodConfig = require('./webpack.prod')
const devConfig = require('./webpack.dev')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
// 定义对象保存 base 配置信息
const commonConfig = {
// 反而没有报错( 相对路径, 相对于content配置路径)
// 这里content没有配置,就找的是package.json里面的webpack config的目录相对路径
entry: './src/index.js',
devtool: 'source-map',
resolve: {
// 添加新的扩展名
extensions: [".js", ".json", '.ts', '.jsx', '.vue'],
// 添加别名
// import Home from './components/Home'
// =>
// import Home from '@/components/Home'
alias: {
// 使用相对路径
'@': resolveApp('./src')
//'@': path.resolve(__dirname, './src')
}
},
output: {
filename: 'js/main.js',
// 输出到哪儿
// 使用相对路径
path: resolveApp('./dist'),
// path: path.join(__dirname, 'dist'),
// 默认空字符串, 浏览量会帮我们添加/
// publicPath: ''
// 自己添加/, 但是这样本地路径会找不到
// publicPath: '/'
// 相对路径, 本地可以访问,但是webpack serve会找不到
// publicPath: './'
// publicPath: '/lg'
// assetModuleFilename: 'img/[name].[hash:4][ext]'
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', {
loader: 'css-loader',
options: {
// 代表向前找一个loader处理
importLoaders: 1,
esModule: false
}
}, 'postcss-loader']
},
{
test: /\.less$/,
use: ['style-loader', {
loader: 'css-loader',
options: {
// 代表向前找一个loader处理
importLoaders: 1,
esModule: false
}
}, 'postcss-loader', 'less-loader']
},
// 全部转成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中的库也使用了babel,导致干扰
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({
// 这里要添加双引号,DefinePlugin会把数据原封不动返回
BASE_URL: '"./"'
}),
// 针对vue-loader > 15的版本,需要手动设置插件
new VueLoaderPlugin()
]
}
module.exports = (env) => {
const isProduction = env.production
// 设置线程环境, 让config中也可以判断环境
process.env.NODE_ENV = isProduction ? 'production' : 'development'
// 依据当前的打包模式来合并配置
const config = isProduction ? prodConfig : devConfig
const mergeConfig = merge(commonConfig, config)
return mergeConfig
}
- 正式环境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默认output.path的文件夹
from: 'public',
globOptions: {
// index.html上面已经操作过了,需要排除。
// 默认从项目根找, **代表从当前目录找
ignore: ['**/index.html']
}
}
]
})
]
}
- 测试环境webpack.dev.js
const resolveApp = require('./paths')
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
module.exports = {
mode: 'development',
devtool: 'cheap-module-source-map',
// 开发阶段屏蔽 .browserslistrc
target: 'web',
// 开启HMR
devServer: {
// 只热加载变化的部分
hot: 'only',
// 开启压缩
compress: true,
// 处理直接向后端请求,导致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',
// 我们打包之后的资源如果说依赖其它的资源,此时就告知去哪找。
// 强烈建议使用绝对路径
directory: resolveApp('./public'),
// 监听directory
watch: true
}
},
plugins: [
// 负责让把具体要使用的插件和webpack结合起来
new ReactRefreshWebpackPlugin()
]
}
- 根据环境配置 babel.config.js
const presets = [
[
'@babel/preset-env',
{
// false: 不对当前的JS处理做 polyfill 的填充
// usage: 依据用户源代码当中所使用到的新语法进行填充
// entry: 依据我们当前筛选出来的浏览器决定填充什么
// 使用entry,需要在使用的地方导入头文件
// import "core-js/stable";
// import "regenerator-runtime/runtime"
useBuiltIns: 'usage',
// 默认版本2
corejs: 3
}
],
// 识别jsx文件
[ '@babel/preset-react' ],
// 识别ts文件
[ '@babel/preset-typescript' ]
]
const plugins = []
// 依据当前的打包模式来决定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"
}
十五、分模块打包
可以方便加载的时候,只是加载当前需要的模块,而不是一起都加入到项目中。
- 多入口
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'
// 同时依赖多个
// 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({
// 默认会生成一个注释文件, 这里去除
extractComments: false,
}),
],
splitChunks: {
// all代表同步和异步加载都分模块
chunks: 'all'
}
}
}
// 会生成767.build.js,767是chunkId
- splitChunks, 静态拆分
splitChunks: {
// all代表同步和异步加载都分模块
// initial代表同步
chunks: 'initial', // async initial all
// 体积大于20KB的进行拆分, 而且拆分后最小20KB
// 一般minSize和maxSize 的值一样
minSize: 20000, // 20KB
maxSize: 20000, // 20KB
// minSize 和 maxSize优先级 高于 minChunks。
// 如果要一起使用,就这样设置
// minChunks代表最少import 引用了一次
minChunks: 1,
// 处理好了, 先放缓存, 后面处理好了, 再看是否合并
cacheGroups: {
// syVendors 和 default 名字随便定义
syVendors: {
// node_modules文件夹里面,代表三方库
// \/代表不同系统里面的路径符号
test: /[\\/]node_modules[\\/]/,
// filename 可以使用占位符
filename: 'js/[id]_vendor.js',
// 优先级, 默认-10。 数字越大优先级越高
priority: -10,
},
default: {
// import 2次的才拆分
minChunks: 2,
filename: 'js/syy_[id].js',
priority: -20,
}
}
}
- 动态导入
动态导入的会进行自动拆分模块
// 动态导入
// 魔法注释, 会把title作为chunk的名字
import(/*webpackChunkName: "title"*/'./title')
// webpack.common.js
output: {
// 设置chunk文件输出名字
chunkFilename: 'js/chunk_[name].js'
},
optimization: {
// natural 按自然数进行编号排序,如果某个文件当前次不再被依赖那么重新打包时序号都会变
// named 建议开发环境使用,名字长会影响性能
// deterministic 生产环境使用
chunkIds: 'deterministic',
minimizer: [
new TerserPlugin({
// 默认会生成一个注释文件, 这里去除
extractComments: false,
}),
]
}
- runtime chunk
可以将加载模块的信息抽取出来, 方便做缓存。
optimization: {
// 将加载模块的信息抽取出来,生成一个新的runtime文件
// runtimeChunk: true,
// 同样的导入信息, 只有一个runtime
runtimeChunk: 'single'
/*其他*/
}
bundle: 直接部署,入口文件
vendor:三方的库,node_modules下面
chunk: 依赖代码块
runtime:连接信息,方便缓存
网友评论