虽然网上有很多webpack多页面相关的内容,但是大部分都很老旧了,用typescript写webpack配置的很基本没有。这里主要是给喜欢typescript的同学提供一点参考。
下面就是正文了
目录结构
├── build // webpack 配置目录
│ ├── helper // webpack配置中的一些工具函数
│ │ ├── constants.ts
│ │ ├── entries.helper.ts
│ │ └── template-compiler.helper.ts
│ ├── loader
│ │ ├── include-html-loader.ts
│ │ └── schema.ts
│ ├── webpack.base.conf.ts
│ ├── webpack.dev.conf.ts
│ └── webpack.prod.conf.ts
├── src
│ ├── assets
│ │ ├── images
│ │ │ └── 1.png
│ │ ├── script
│ │ │ ├── a.ts
│ │ │ └── b.ts
│ │ └── style
│ │ └── common.scss
│ ├── components
│ │ ├── footer.html
│ │ └── header.html
│ └── pages
│ ├── index.html
│ ├── index.scss
│ ├── index.ts
│ ├── page-a
│ │ ├── index.html
│ │ ├── index.scss
│ │ ├── index.ts
│ │ └── sub-page-a
│ │ ├── index.html
│ │ ├── index.ts
│ │ └── sub-sub-page-a
│ │ ├── index.html
│ │ └── index.ts
│ ├── page-b
│ │ ├── index.html
│ │ └── index.ts
│ └── page-c
│ ├── index.html
│ └── index.ts
├── package-lock.json
├── package.json
└── tsconfig.json
开始
采用的是现在比较流行的分环境,分文件的方式
webpack 配置
// webpack.base.conf.ts
import * as webpack from 'webpack';
import {getEntries} from './helper/entries.helper';
import * as HtmlWebpackPlugin from 'html-webpack-plugin';
import {templateCompilerConfig} from './helper/template-compiler.helper';
import {ASSETS_DIR} from './helper/constants';
import * as ManifestPlugin from 'webpack-manifest-plugin';
import {resolve} from 'path';
const config: webpack.Configuration = {
entry: getEntries(), // 配置多入口的helper函数, 后面会详细讲解
resolve: {
alias: {
src: resolve(__dirname, '../src/')
},
// webpack编译时匹配的文件后缀,很关键
// 有些同学不能识别ts文件就是因为没有配置该项
extensions: ['.ts', '.js']
},
// 常规的用loader来处理各种文件
module: {
rules: [
{
test: /\.ts$/,
exclude: /(node_modules|build)/,
include: /src/,
use: [
{
loader: 'ts-loader'
}
]
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
{
loader: 'sass-loader',
// sass-loader编译用的是dart-sass的实现
options: {implementation: require('sass')}
},
{
loader: 'sass-resources-loader',
options: {
resources: ['./src/assets/style/common.scss']
}
}
]
},
{
test: /\.html$/,
use: [
'html-loader',
// {
// loader: resolve(__dirname, './loader/include-html-loader.ts')
// }
// 这个地方是我自己写的一个loader,开始是集成在项目中,后来提取成一个npm包了
'include-template-loader'
]
},
{
// 处理图片,小于5k的直接base64编译到html中
test: /\.(png|jpg|gif|jpeg|svg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 5 * 1024,
name: `${ASSETS_DIR}/img/[name].[hash:6].[ext]`
}
}
]
},
{
// 处理字体文件,和上面一样的方案
test: /\.(woff2?|eot|ttf|otf)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 5 * 1024,
name: `${ASSETS_DIR}/font/[name].[hash:6].[ext]`
}
}
]
}
]
},
plugins: [
// 多入口的html-webpack-plugin处理,后面详细讲解
...templateCompilerConfig().map(config => new HtmlWebpackPlugin(config)),
new ManifestPlugin(),
// 注册全局的依赖
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
],
// 优化,提取公共的包
optimization: {
minimize: true,
splitChunks: {
cacheGroups: {
vendor: {
chunks: 'initial',
test: /node_modules/,
name: 'vendor',
minSize: 0,
priority: 10
},
common: {
chunks: 'initial',
name: 'common',
minSize: 0,
minChunks: 2 // 两个文件引用了就生成一个common包
}
}
}
}
};
export default config;
// webpack.prod.conf.ts
import * as webpack from 'webpack';
import {resolve} from 'path';
import * as MiniCssExtractPlugin from 'mini-css-extract-plugin';
import {CleanWebpackPlugin} from 'clean-webpack-plugin';
import * as merge from 'webpack-merge';
import config from './webpack.base.conf';
import {ASSETS_DIR, PUBLIC_PATH} from './helper/constants';
const prodConfig: webpack.Configuration = {
mode: 'production',
output: {
path: resolve(__dirname, '../dist'),
filename: `${ASSETS_DIR}/js/[name].[hash:6].bundle.js`,
publicPath: PUBLIC_PATH
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, // 生产环境提取css到文件
'css-loader',
'postcss-loader',
{
loader: 'sass-loader',
options: {implementation: require('sass')}
},
{
loader: 'sass-resources-loader',
options: {
resources: ['./src/assets/style/common.scss']
}
}
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: `${ASSETS_DIR}/css/[name].[hash:6].css`,
chunkFilename: `${ASSETS_DIR}/css/[id].[hash:6].css`
})
]
};
export default merge(config, prodConfig);
// webpack.dev.conf.ts
import * as webpack from 'webpack';
import * as merge from 'webpack-merge';
import config from './webpack.base.conf';
import {resolve} from 'path';
import {ASSETS_DIR, PUBLIC_PATH} from './helper/constants';
const devConfig: webpack.Configuration = {
output: {
path: resolve(__dirname, '../dist'),
filename: `${ASSETS_DIR}/js/[name].bundle.js`, // 开发环境没有加hash
publicPath: PUBLIC_PATH
},
mode: 'development',
devtool: 'eval-source-map',
// 用webpack-dev-server 实现开发服务器
devServer: {
port: 4000
// writeToDisk: true
}
};
export default merge(config, devConfig);
上面都是一些常规的配置,下面来介绍多页面的入口的处理
处理多页面的入口
// helper/entries.helper.ts
import {readdirSync, statSync} from 'fs';
import {join, sep} from 'path';
import {SOURCE_PATH} from './constants';
// 获取所有的入口文件的路径,用递归的方式处理
export const getEntryFiles = () => {
const getFiles = (baseUrl: string, files: string[] = []) => {
// 读取文件夹
readdirSync(baseUrl)
// 处理成绝对路径
.map(item => join(baseUrl, item))
// 遍历路径
.forEach(item => {
// 如果是文件夹,递归调用
if (statSync(item).isDirectory()) {
getFiles(item, files);
// 如果是index.js || index.ts 就是我们要找的入口文件路径,放入数组中
} else if (/index\.[tj]s/.test(item)) {
files.push(item);
}
});
return files;
};
// 返回所有合法的入口路径
return getFiles(SOURCE_PATH);
};
// 构建入口对象
export const getEntries = () => {
// 存放最终入口的对象
const entries: {[key: string]: string} = {};
getEntryFiles().forEach(path => {
const entryName = getEntryName(path);
entries[entryName] = path;
});
return entries;
};
// 获取入口的名称,该方法主要是为了编译后还能保持现有的目录结构
// 很符合直觉的在html中通过相对路径的方式跳转到其他页面,网上很多方案都不是很符合直觉
export const getEntryName = (path: string) => {
const basename = path.replace(SOURCE_PATH, '').replace(sep, '');
// 在windows中目录分割符是\, 统一替换成 /
return basename.substring(0, basename.lastIndexOf('.')).replace(/\\/g, '/');
};
处理html-webpack-plugin
import {getEntryFiles, getEntryName} from './entries.helper';
import * as HtmlWebpackPlugin from 'html-webpack-plugin';
// 返回入口文件对应的html模版
export const templateCompilerConfig: () => HtmlWebpackPlugin.Options[] = () => {
return getEntryFiles().map(path => {
// 通过正则匹配的方式,替换ts||js后缀为html
const template = path.replace(/[^\.]*(?!.*\.)/, 'html');
// 符合直觉的相对路径跳转html的关键
const name = getEntryName(template);
return {
template: template,
inject: true,
filename: `${name}.html`,
chunks: ['vender', 'common', name]
};
});
};
以上就是所有的webpack配置,没有用到Babel,也没有polyfill,如果还需要其他功能可以自行发挥。
本项目主要解决了多页面应用a标签跳转的问题,不需要在写html模版的时候用到一些特殊的写法,非常符合直觉。
最后在添加npm script
"dev": "webpack-dev-server --config ./build/webpack.dev.conf.ts"
,
"build": "webapck --config ./build/webpack.prod.conf.ts"
webpack 本身就有对ts配置文件的支持,只需要安装ts-node就行了
版权声明:本文为huzzzz原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。