webpack
随着前端技术的发展,前后端开发的分离,前端项目的构建显得越来越有必要,而 Webpack 就是一个前端的打包工具,帮助我们完成前端项目的构建。
前端的项目构建工具也逐渐的由借助其他语言的构建工具,到使用 Grunt,后来被 Stream 概念的 Gulp 所取代,再到现在的 Rullup, Webpack。而 Webpack 也成为了三大框架官方脚手架所使用的构建工具,在我们做项目的时候会经常接触到,非常有学习了解的必要。
其起源于作者在 Google 就职时,后项目被 Instagram 支持。
其各个版本更迭情况大概如下:
- v1
- 编译,打包
- 模块热更新
- 代码分割
- 文件处理
- v2
- Tree Sharking
- 支持 ES Module
- 动态 Import()
- v3
- Scope Hoisting
- Magic Comments
- v4
- 零配置
- mode
- 新增支持的 js 模块
- 插件的改变和优化
Webpack 的作用,用官网的一张图就能概括:
核心概念
v3 中有四个核心概念:entry,output,loader,plugin
entry
entry 是 webpack 进行打包的入口,webpack 会从入口开始去寻找入口文件的依赖项进行打包。
output
output 是 webpack 打包后的输出文件。
loader
webpack 本身只能打包 js,而其他类型的文件就要借助于各种各样的 loader。
plugin
插件可以帮助我们实现更多样的功能。
打包普通 js
npm init
npm install webpack@^3.0.0
touch webpack.config.js
mkdir src
touch src/app.js
touch src/sum.js
app.js:
import sum from './sum'
console.log(sum(1 + 2))
sum.js:
export default function (a, b) {
return a + b
}
webpack.config.js
module.exports = {
entry: {
app: './src/app.js'
},
output: {
// output 中可以使用占位符
filename: '[name].[hash].js',
path: __dirname + '/dist'
}
}
运行 webpack 打包命令即可成功打包。
打包 ES6
npm init
npm install webpack
// babel包会在下面解释作用
npm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env @babel/polyfill @babel/runtime-corejs3 @babel/plugin-transform-runtime core-js regenerator-runtime
touch webpack.config.js
touch .babelrc
mkdir src
touch src/app.js
touch src/sum.js
app.js:
import sum from './sum'
let a = 1
const b = 3
const c = [...[1, 2], 3]
console.log(sum(a + b + c[0]))
sum.js:
export default (a, b, c) => a + b + c
为了让 webpack 识别并打包 ES6 语法,我们就需要使用 babel 和 babel-loader 了。
如果要使用 babel v7 版本,则 babel-loader 需要安装 babel-loader@8.0.0-beta.0
webpcak.config.js
module.exports = {
entry: './src/app.js',
output: {
filename: '[name].[hash].js',
path: __dirname + '/dist'
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: '/node_modules/'
}
]
}
}
.babelrc
{
"presets": ["@babel/preset-env"]
}
我们在 rules 里配置了 .js 结尾的文件调用 babel-loader。使用了 @babel-core(核心) 和 配置了 @babel/preset-env (识别ES6语法),
执行 webpack 命令打包,ES6 语法正确转译:
@babel/preset-env 只是让babel 支持了新的 ES6 语法,但是 ES6 中新加的功能在一些低级的浏览器中并不会被支持,这就需要使用到我们安装的其他的 babel 包了。下面说到的包其实都是必须要安装到 --save 中的, 因为这些包都是要在应用中使用到的(@babel/plugin-transform-runtime 为 dev,搭配 @babel/runtime 使用)
其主要分为两类使用:
@babel/polyfill
@babel/polyfill 其是就是一个功能包,里面帮我们实现了 ES6 的各种 API,我们只需要在入口文件中引入即可:
import 'babel-polyfill'
注意这样我们的打包生成文件会大上许多,多出来的就是我们引入的全部的 polyfill 的大小,另外,引入的 polyfill 是会污染全局变量的。
使用core-js v2 的时候可以像上面引入。
core-js 到了 v3,@babel/polyfill 被废弃了,改而要使用 core-js regenerator-runtime,之前的全部引入 polyfill 也要改成:
import 'core-js/stable';
import 'regenerator-runtime/runtime';
在 babel 中,还可以通过 preset-env 中的配置来实现 polyfill 的按需引入来节省空间:
.babelrc:
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
// 指定core-js为v3
"corejs": 3
}
]
]
}
@babel/runtime & @babel/plugin-transform-runtime
借助于 @babel/runtime & @babel/plugin-transform-runtime 同样可以实现支持 ES6 API,并且这种方式是按需引入的,还不会污染全局变量。
之前的说法是这种方式比较适合开发库使用,但是这种方式是有缺陷的,如 [1,2,3].includes(1) 这种依赖于 Array.prototype.includes 的方法就不支持。所以在产品中还是使用 polyfill 的方式为好。
但是在新版本中可以指定 core-js 为 v3(对应的 runtime 包为 @babel/runtime-corejs3),就支持了,所以目前来看还是 runtime 的方法最优。
使用时也只要在 .babelrc 中配置:
{
"presets": [
"@babel/preset-env"
],
"plugins": [
["@babel/plugin-transform-runtime", {
"corejs": {
"version": 3,
"proposals": true
}
}]
]
}
打包 typescript
打包 ts 我们要借助 ts-loader 和 tsconfig.json(ts 编译设置文件)。
但是随着babel的发展,其实是更推荐使用 babel 的方式去转译 ts。至于为什么,我想把答案交给下面这篇译文:juejin.cn/post/684490…
ts-loader
npm init
// 最新的ts-loader要求webpackv4
npm install webpack@^4.0.0 webpack-cli typescript ts-loader
touch tsconfig.json
touch webpack.config.js
mkdir src
touch src/app.ts
app.ts:
const a = 45
interface Cat {
name: String,
sex: String
}
function touchCat(cat: Cat): void {
console.log('miao, I am ' + cat.name)
}
touchCat({
name: 'kafe',
sex: 'male'
})
webpack.config.js:
module.exports = {
mode: 'development',
entry: './src/app.ts',
output: {
filename: '[name].[hash].js',
path: __dirname + '/dist'
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader'
}
]
}
}
tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"allowJs": true
},
"include": [
"./src/*"
],
"exclude": [
"./node_modules"
]
}
@babel/preset-typescript
npm init
npm install webpack@^3.0.0 babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env @babel/preset-typescript
touch webpack.config.js .babelrc
mkdir src
touch src/app.ts
app.ts:
const a = 45
interface Cat {
name: String,
sex: String
}
function touchCat(cat: Cat): void {
console.log('miao, I am ' + cat.name)
}
touchCat({
name: 'kafe',
sex: 'male'
})
webpack.config.js:
module.exports = {
entry: './src/app.ts',
output: {
filename: '[name].[hash].js',
path: __dirname + '/dist'
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'babel-loader',
exclude: '/node_modules/'
}
]
}
}
.babelrc:
{
"presets": ["@babel/preset-env", "@babel/preset-typescript"]
}
打包后如下:
注意这样因为没有使用 tsc 编译,所以是没有类型检查的,如果需要这个功能,可以使用webpack中的 ts 类型检查插件。