本文使用Webpack 4和Typescript 3构建一个简单项目,旨在梳理基础的项目结构,常规的配置选项以及开发打包的流程。更多复杂和高级的选项则可以在此基础上进行扩展。
搭建项目
- 创建项目
mkdir webpack-sample
cd webpack-sample
npm init -y
- 安装依赖
- typescript
npm install --save-dev typescript
- lodash
npm install --save lodash
npm install --save-dev @types/lodash
- webpack
npm install --save-dev webpack webpack-cli
npm install --save-dev ts-loader
npm install --save-dev clean-webpack-plugin html-webpack-plugin webpack-bundle-analyzer
- 项目结构
webpack-sample
|-- src
| |-- index.html // template
| |-- main.ts // entry
| |-- card.ts
|-- webpack.config.js
|-- tsconfig.json
|-- package.json
|-- package-lock.json
源码内容
- index.html
- 基础模板,提供一个空的container
<!DOCTYPE html>
<html>
<head>
<title>Webpack Sample</title>
</head>
<body>
<div id="container"></div>
</body>
</html>
- card.ts
- 定义一个Card的class,用于生成Card元素
// card.ts
export class Card {
title: string;
desc: string;
constructor(title: string, desc: string) {
this.title = title;
this.desc = desc;
}
genElement() {
const card = document.createElement('div');
const title = document.createElement('h2');
title.innerHTML = this.title;
const desc = document.createElement('div');
desc.innerHTML = this.desc;
card.appendChild(title)
card.appendChild(desc);
return card;
}
}
- main.ts
- 引入lodash和Card类
- 循环生成多个Card元素并添加到页面中
// main.ts
import lodash from 'lodash';
import { Card } from './card';
const container = document.getElementById('container');
const cardList = [{
title: 'Title A',
desc: 'test test test test'
}, {
title: 'Title B',
desc: 'longgggggggggggggggggggggggggggggggtest'
}]
lodash.each(cardList, cardInfo => {
const card = new Card(cardInfo.title, cardInfo.desc);
const cardEle = card.genElement();
container.appendChild(cardEle);
})
配置信息
- tsconfig.json
- esModuleInterop选项是为了支持lodash引入时使用ES Module标准
// tsconfig.json
{
"compilerOptions": {
"target": "ES5",
"esModuleInterop": true
}
}
- webpack.config.js
- resolve配置了webpack寻找module的方式
- 针对ts文件,用ts-loader来加载
- clean-webpack-plugin清理输出路径
- html-webpack-plguin用于处理html模板
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
main: './src/main.ts'
},
resolve: {
extensions: ['.ts', '.js']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js',
},
module: {
rules: [
{
test: /\.ts/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
}
- 脚本
// package.json
{
"scripts": {
"build": "webpack"
}
}
运行npm run build进行编译,在dist目录生成输出文件。双击index.html可以在浏览器中看到生成的页面。
样式表
- 为上述示例添加样式表
- 添加src/card.scss文件,编辑内容
// card.scss
.card {
padding: 4px;
margin: 4px;
border: 1px solid #333333;
.desc {
word-break: break-all;
}
}
- 修改src/card.ts文件,为生成的元素添加一些属性
// card.ts
genElement() {
const card = document.createElement('div');
const title = document.createElement('h2');
title.innerHTML = this.title;
const desc = document.createElement('div');
desc.className = 'desc'; // 添加desc类名
desc.innerHTML = this.desc;
card.appendChild(title)
card.appendChild(desc);
card.className = 'card'; // 添加card类名
return card;
}
- 在webpack打包流程中加入样式表处理
- 安装依赖
npm install --save-dev style-loader css-loader sass-loader node-sass
- 修改配置
// webpack.config.js
module.exports = {
resolve: {
extensions: ['.ts', '.js', '.scss']
},
rules: [
{
test: /\.s[ac]ss/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}
]
}
- 引入模块
// main.ts
import './card.scss';
- 测试效果
- 运行
npm run build,然后在dist目录中打开index.html文件,可以看到样式已经应用到页面上 - 可以看到dist目录中并没有css文件,因为style-loader默认使用的方式是styleTag,即用js将样式以style标签的形式写入到html中
- 使用linkTag添加样式
- 上述配置方式是将样式直接添加到html中,对于样式表较多的情况不是很合适
- 使用独立的样式表,以link标签添加到html中,方法如下
- 安装插件mini-css-extract-plugin
npm install --save-dev mini-css-extract-plugin
- 修改配置
// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
module: {
rules: [
{
test: /\.s[ac]ss/,
use: [
// 'style-loader',
MiniCssExtractPlugin.loader, // 使用MiniCssExtractPlugin.loader替换style-loader
'css-loader',
'sass-loader'
],
exclude: /node_modules/
}
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'}),
new MiniCssExtractPlugin(), // 该插件需要置于html插件之后
]
}
- 再运行
npm run build就会生成独立的css文件,并将linkTag添加到index.html中
- css后处理
- 利用postcss-loader可以对css进行处理。例如利用autoprefixer为css添加浏览器前缀,配置如下
// package.json中配置目标浏览器
"browserslist": ["last 2 versions"]
// 添加postcss.config.js
module.exports = {
plugins: [require('autoprefixer')]
}
// webpack.common.js
module: {
rules: [
{
test: /\.s[ac]ss/,
use: [
// 'style-loader',
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader', // 位置在css-loader之后
'sass-loader'
],
exclude: /node_modules/
}
]
}
sourceMap
- 上述配置中没有开启sourceMap,对于开发调试不是很方便。例如,我们人为构造报错:
// card.ts
genElement() {
if (this.desc.toLowerCase() === 'error') {
throw Error('custom error');
}
...
}
// main.ts
const cardList = [
{
title: 'Title Error',
desc: 'error',
}
];
若未开启sourceMap,则需要到bundle.js中进行查错,很不方便。
- 开启sourceMap需要修改tsconfig.json和webpack.config.js
// tsconfig.json
{
"sourceMap": true
}
// webpack.config.js
module.exports = {
devtool: 'cheap-module-eval-source-map'
}
- devtool还有更多选项,可以参考
- 如果配置mode为'development'(默认是'production', 不进行显式配置会提示warning),则相当于配置了devtool为'eval',例如
// webpack.config.js
module.exports = {
mode: 'development',
// devtool: 'eval'
}
webpack-dev-server
- webpack-dev-server可以提供一个简单的web服务器对webpack打包后的文件伺服
- 首先安装依赖
npm install --save-dev webpack-dev-server
- 然后修改配置
// webpack.config.js
module.exports = {
devServer: {
contentBase: './dist'
}
}
- 增加脚本
// package.json
{
"scripts": {
"start": "webpack-dev-server --open"
}
}
运行npm run start开始webpack打包流程并启动服务器,并打开网页查看。
- 使用webpack-dev-server后默认不会在dist目录生成打包后的文件,而是将生成结果直接读入内存,然后在webpack-dev-server的根目录下进行伺服。
环境区分
- 上述配置基本都是按照开发环境来进行的,针对生产环境需要使用不同的配置,例如不要source-map,压缩代码等
- 可以按照文档指引来配置不同环境下的内容,主要流程即配置不通的config文件,然后在不同环境下指定使用不同配置文件
- mode选项设置为development和production时相当于配置了很多默认内容:参考文档
- 生产环境的配置摘要
- 静态文件名加上hash串
// webpack.common.js output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[contenthash].js' }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].[contenthash].css' }), ]- js代码压缩:production模式下会自动开启minimize的优化选项,默认会使用terser-webpack-plugin进行js代码压缩
- css代码压缩:虽然文档中说需要额外使用插件对css进行压缩,但是实际实验之后发现设定production模式后,css默认就已经压缩了
总结
至此,我们的项目已经构建完成,总结如下:
- 搭建了基本的项目结构,包括模板,ts脚本,scss样式表
- 配置了常规的构建打包的流程
- 支持开发环境的调试需求,包括开发服务器,sourceMap等
- 支持生产环境的打包需求