文章目录
Webpack
本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle。
依赖图(dependency graph):
每当一个文件依赖另一个文件时,webpack 都会将文件视为直接存在 依赖关系。这使得 webpack 可以获取非代码资源,如 images 或 web 字体等。并会把它们作为 依赖 提供给应用程序。
当 webpack 处理应用程序时,它会根据命令行参数中或配置文件中定义的模块列表开始处理。 从 入口 开始,webpack 会递归的构建一个 依赖关系图,这个依赖图包含着应用程序中所需的每个模块,然后将所有模块打包为少量的 bundle —— 通常只有一个 —— 可由浏览器加载。
相关概念
安装和初始化
npm install --save-dev webpack webpack-cli
我们在webpack.config.js 文件中定义了 webpack 的功能,我们使用如下内容初始化它:
const path = require('path')
const config = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'main.js'
}
}
module.exports = config
然后我们将定义一个名为build 的新 npm 脚本,该脚本将执行与 webpack 的捆绑:
// ...
"scripts": {
"build": "webpack --mode=development"
},
// ...
Bundling绑定
尽管 ES6模块是在 ECMAScript 标准中定义的,老一些的浏览器并不知道如何处理划分为模块的代码。由于这个原因,被划分为模块的代码对于浏览器必须是 绑定 的,这意味着 所有的源代码文件都被转换成一个包含所有应用代码的文件。
实际上,进行绑定是为了为应用定义一个入口点,通常是index.js 文件。 当 webpack 打包代码时,它包含入口点导入的所有代码,以及导入代码的导入,等等。
将应用的代码划分为多个文件的老方法是基于这样一个事实:即index. html 文件在脚本标记的帮助下加载了应用的所有单独的 JavaScript 文件。 这导致性能下降,因为每个单独文件的加载都会导致一些开销。 出于这个原因,现在的首选方法是将代码捆绑到单个文件中。
配置文件
const path = require('path')
const config = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'main.js'
}
}
module.exports = config
配置文件使用 JavaScript 编写,配置对象使用 Node 的模块语法导出。
配置对象的entry属性指定将作为绑定应用的入口点的文件。
属性定义了将要存储绑定代码的位置 output。 目标目录必须被定义为绝对路径,这很容易用path.resolve方法创建。 我们还使用了__dirname
,它是 Node 中的一个全局变量,用于存储到工作目录的路径。
Loaders装载器
babel-loader
我们可能需要一个适当的 loader 来正确捆绑App.js 文件。 默认情况下,webpack 只知道如何处理普通的 JavaScript。尽管我们可能没有意识到这一点,但我们实际上正在使用JSX在 React 中渲染我们的视图。
我们可以使用装载器来告知 webpack 需要在捆绑之前处理的文件。
让我们为应用配置一个装载器,将 JSX 代码转换为常规的 JavaScript:
const config = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'main.js',
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react'],
},
},
],
},
}
test 属性指定加载程序用于名称以.js 结尾的文件。 属性指定对这些文件的处理将通过babel-loader来完成。options 属性用于为加载程序指定参数,用于配置其功能。
让我们将装载器及其所需的包作为开发依赖项 安装:
npm install @babel/core babel-loader @babel/preset-react --save-dev
如果捆绑的应用的源代码使用async/await
,浏览器将不会在某些浏览器上渲染任何内容。 谷歌在控制台中搜索错误信息将会在这个问题上给出一些答案。 我们必须再安装一个缺失的依赖项,即@babel/polyfill
:
npm install @babel/polyfill
让我们对webpack.config.js 文件中的 webpack 配置对象的entry 属性进行如下更改:
entry: ['@babel/polyfill', './src/index.js']
Transpilers转译工具
将代码从一种 JavaScript 形式转换为另一种 JavaScript 形式的过程称为transpiling。 该术语的一般定义是通过将源代码从一种语言转换为另一种语言来编译源代码。
通过使用上一节中的配置,我们在babel的帮助下将包含 JSX 的代码转换为常规 JavaScript,这是目前最流行的工具。
大多数浏览器不支持 ES6和 ES7中引入的最新特性,因此代码通常会转移到实现 ES5 标准的 JavaScript 版本中。
通过plugins 定义了 Babel 执行的转译过程。 实际上,大多数开发人员使用的是现成的预设插件,这些插件是一组预先配置的插件。
目前,我们正在使用@babel/preset-react预设来转译我们应用的源代码:
{
test: /\.js$/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react']
}
}
让我们添加一个@babel/pressing-env
插件,它包含使用所有最新特性编写代码并将其转化为兼容 ES5标准的代码所需的所有内容:
{
test: /\.js$/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react']
}
}
让我们用下面的命令来安装预置:
npm install @babel/preset-env --save-dev
style-loader/css-loader
当使用 CSS 时,我们必须使用CSS和style装载器:
{
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react', '@babel/preset-env'],
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
];
}
css loader的工作是加载CSS 文件, style loader的工作是生成并注入一个style 元素,该元素包含应用的所有样式。
使用这种配置,CSS 定义包含在应用的main.js 文件中。 出于这个原因,不需要单独导入应用的主要index. html 文件中的CSS 样式。
如果需要,应用的 CSS 也可以通过使用mini-CSS-extract-plugin生成到它自己的独立文件中。
当我们安装装载器时:
npm install style-loader css-loader --save-dev
Webpack-dev-server
当前的配置使得开发我们的应用成为可能,但是工作流非常糟糕(以至于它类似于 Java 的开发工作流)。 每次我们对代码进行修改时,我们必须将它捆绑起来并刷新浏览器以测试代码。
webpack-dev-server
为我们的问题提供了一个解决方案:
npm install --save-dev webpack-dev-server
让我们定义一个 npm 脚本来启动 dev-server:
{
// ...
"scripts": {
"build": "webpack --mode=development",
"start": "webpack serve --mode=development"
},
// ...
}
我们还可以在webpack.config.js 文件的配置对象中添加一个新的devServer 属性:
const config = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'main.js',
},
devServer: {
contentBase: path.resolve(__dirname, 'build'),
compress: true,
port: 3000,
},
// ...
};
Npm start 命令现在将在端口3000启动 dev-server,这意味着我们的应用将可以通过浏览器中的 http://localhost:3000 访问。 当我们修改代码时,浏览器会自动刷新页面。
更新代码的过程很快。 当我们使用 dev-server 时,代码不会以通常的方式捆绑到main.js 文件中。 捆绑的结果只存在于内存中。