首先来简单介绍一下webpack:现代 Javascript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个会映射项目所需的每个模块 的依赖图(dependency graph),并生成一个或多个 bundle。webpack4.0出现之后,我们可以不用再引入一个配置文件来打包项目,并且它仍然有着很高的可配置性,可以很好满足我们的需求。
在开始正文之前,首先先来看看我们要实现的成果:
-
支持ES6+JQuery+Less/Scss的单页/多页脚手架
-
支持ES6+React+Less/Scss+Typescript的单页/多页脚手架
-
支持ES6+Vue+Less/Scss+Typescript的单页/多页脚手架
基于webpack4.0搭建的脚手架(支持react/vue/typescript/es6+/jquery+less/scss)
在脚手架的开发过程中我会详细介绍每个插件或者loader的用途以及webpack的核心理念,如有不懂或者有其他更好的想法欢迎交流。
下面是基于该文章的webpack4.0的思维导图:
-
入口:指示 webpack 应该使用哪个模块作为入口起点
-
输出:告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件
-
loader:让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用
-
插件:用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量
-
模式:通过选择 development, production 或 none 之中的一个,来设置 mode 参数,从而进行不同的打包优化
-
浏览器兼容性:支持所有符合ES5 标准的浏览器(不支持 IE8 及以下版本)
下面提供官网的打包模型
在实现脚手架之前,假设我们已经创建了目录和package.json文件,接下来先安装webpack相关依赖:
// 此处建议安装局部依赖,安装全局依赖可能会出现版本问题
npm install -D webpack webpack-cli
因为项目要支持es6+,我们还需要安装babel相关依赖:
npm install -D babel-loader @babel/core @babel/preset-env
这个时候可以开始配置我们的脚手架逻辑了,为了项目结构清晰易于维护,我们建一个build目录专门放webpack构建的脚本,webpack默认的配置文件是webpack.config.js,由于实际项目需要,我们将其拆分为3个文件,分别是webpack通用配置文件webpack.base.js,开发环境配置文件webpack.dev.js以及生产环境配置文件webpack.prod.js。
我们先处理webpack.common.js文件
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
main: './src/index.js',
},
output: {
path: path.resolve(__dirname, '../dist'),
},
module: {
rules: [
// 将es6编译成es5
{
test: /.jsx?$/, // ?表示x有0个或一个
exclude: /node_modules/, // 不编译某个目录下的文件
include: path.resolve(__dirname, '../src'), // 只在include包含的目录下进行loader编译
use: [
"babel-loader",
]
},
]
}
}
为了项目后期的开发和维护,我们建立好项目结构:
public目录是事先准备好的html模版,这里就不介绍了,其他目录可根据具体项目进行设置。
我们还需要一个插件将打包后的文件植入到html模版中并导出到dist目录下,这里使用html-webpack-plugin来实现
npm install -D html-webpack-plugin
现在webpack.base.js为如下:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const template = path.resolve(__dirname, '../public/index.html');
module.exports = {
entry: {
main: './src/index.js'
},
output: {
path: path.resolve(__dirname, '../dist')
},
module: {
rules: [
// 将es6编译成es5
{
test: /.jsx?$/, // ?表示x有0个或一个
exclude: /node_modules/, // 不编译某个目录下的文件
include: path.resolve(__dirname, '../src'), // 只在include包含的目录下进行loader编译
use: [
"babel-loader",
]
},
]
},
plugins: [
new HtmlWebpackPlugin({
template,
filename: 'index.html'
})
]
}
为了打包项目,我们需要在webpack.prod.js目录下进行配置,此处需要一个模块webpack-merge将wepack基础配置合并进生产配置,我们先来安装一下:
npm install -D webpack-merge
webpack.prod.js配置如下:
const merge = require('webpack-merge');
const base = require('./webpack.base');
module.exports = merge({
mode: 'production',
output: {
filename: 'js/[name]_[contenthash].js', // 入口和内容hash组成的文件名,也可以是hash
chunkFilename: 'js/[name]_[contenthash].chunk.js'
}
}, base)
然后在package.json里添加执行脚本:
"scripts": {
"build": "webpack --config ./build/webpack.prod.js"
}
webpack默认会找名为webpack.config.js的文件,由于我们将其拆解为prod和dev,所以我们要手动指定webpack执行的文件,添加–config,即可手动指定目录。下面我们开始写一段代码试试吧,在index.js中写入如下es6代码:
// index.js
let name = 'xuxi';
let say = (name) => {
alert(name)
};
say(name);
下面我们执行:
npm run build
此时我们会看见项目中多了一个dist目录,里面的html也植入了相应的代码,
在浏览器中打开:
ok,第一步完成。
下一步是支持css,我们先安装如下几个模块:
npm install --save-dev css-loader style-loader
在webpack.base.js中的module中添加如下代码:
module: {
rules: [
// 将es6编译成es5
{
test: /.jsx?$/, // ?表示x有0个或一个
exclude: /node_modules/, // 不编译某个目录下的文件
include: path.resolve(__dirname, '../src'), // 只在include包含的目录下进行loader编译
use: [
"babel-loader",
]
},
// 加载css
{
test: /.css$/,
use: ['style-loader', 'css-loader'],
},
]
}
注意,laoder的加载顺序是从下往上,从右往左的,所以配置loader的时候要注意一下顺序。
此时在styles目录下加入app.css,在js中引入:
// app.css
#root {
background-color: #f0c;
height: 100px;
}
// index.js
import './styles/app.css'
此时打开浏览器,可以看到css生效了:
现在css导入虽然生效了,但是是有js动态创建添加到head里面的,如果后期项目复杂了,将会严重影响项目的加载速度,所以我们这里需要另一个插件,对css进行代码分割,单独生成css文件:
npm isntall mini-css-extract-plugin -D
根据该插件的官方配置,我们需要把style-loader替换成该插件提供的loader,并配置导出的css文件目录和文件名,为了提高开发环境构建速度,我们只在生产环境分割css:
// webpack.prod.js
const merge = require('webpack-merge');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const base = require('./webpack.base');
module.exports = merge({
mode: 'production',
output: {
filename: 'js/[name]_[contenthash].js', // 入口和内容hash组成的文件名,也可以是hash
chunkFilename: 'js/[name]_[contenthash].chunk.js'
},
module: {
rules: [
{
test: /.css$/,
use: [ // loader解析的顺序是从下到上,从右到左的顺序
{
loader: MiniCssExtractPlugin.loader,
options: {
filename: '[name].css',
chunkFilename: '[name].css',
publicPath: '../' /
}
}
该文件有很多灵活的配置项,大家如果想了解更多可以去typescript官网上查看相关文档。
至此,所有的配置都完成了,是不是很累?哈哈,当然脚手架中还存在一些优化的地方,欢迎大家可以一起完善。
未完成的优化点:-
vue文件内部style无法独立抽出样式,只能通过引入css文件的方式加载样式
-
公用css文件问题:多页面打包时,如果都引入了同一个css,无法服用这个css,希望能将这个css文件作为一个公共模块单独引用
最后,欢迎一起探讨前端的魅力:
更多推荐- 基于create-react-app打包编译自己的第三方UI组件库并发布到npm
- 《前端实战总结》之使用CSS3实现酷炫的3D旋转透视
- 《前端实战总结》之设计模式的应用——备忘录模式
- 《前端实战总结》之使用postMessage实现可插拔的跨域聊天机器人



