webpack
webpack 简介
有说 webpack 是打包机,也有人说它是加工厂,我总感觉它像一个高级的女秘书。今天的故事又开始了:
话说有位上世纪80年代下海创业成功的大老板,但是呢,文化程度不高,随着历史的车轮滚滚向前,也随着公司越做越大,公司的业务也在逐步扩大,准备开拓海外市场,他想找个翻译最好也能辅助一下自己的工作,一是托朋友四处打探,终于找到了这么一位年轻貌美,能力极强的秘书。经过老板与秘书的配合,最终成功拓展了海外市场,让公司做大做强。(故事纯属瞎....,我就不信有雷同)
女秘书的能力到底有哪些呢?
- 翻译能力
- 文件整理能力
- 关联文件,工具的查找与使用的能力
- 归纳总结的能力
- 思维灵活
我们都知道,构建工具 webpack 现如今也是各大公司炙手可热的"大红具",虽然各种工具还在层出不穷,但 webpack 至少现在还是年富力强,所以还是学学吧。
webpack 的能力:
- 编译能力:webpack 可以把前面提到 JSX 等浏览器不能识别的代码编译成 JavaScript,Babel 也支持 ESNext 句法,一句话就是 webpack 可以把 各种文件编译成浏览器可以识别的代码。
- 代码整理能力:webpack 可以把代码拆分,进行按需加载,也可以把代码合并,简化代码,让文件更少,更小。
- 依赖管理:webpack 能把各个文件间的依赖逐一进行解读,编译,整合,以及一些三方插件的引入。
- 模块管理:构建高效,简单,可复用的组件,最后将组件合并为一个完整的应用。
- 热更新能力:监听源码变化,立即更新有变化的模块。这一点在前端的开发,调试中省去了多少的 Ctrl + F。
webpack 构建项目
我们通过构建一个 React 项目来进一步了解 webpack。
- 我们先打开终端,在合适的位置上创建一个 weekplan-app 项目文件夹;
bash
mkdir weekplan-app
cd weekplan-app
- 创建项目 和 package.json 文件;
csharp
npm init -y
此刻可以看看项目文件夹里边是不是多了 package.json 文件了。
-
安装 react 和 reactDOM;
npm install react react-dom serve
目前,weekplan-app 文件夹下就多了 node_modules 文件夹,package-lock.json 文件;
- 打开代码编辑器我们来把项目文件按如下结构补全;
- 在 originalData 下创建 planList.json 文件:
css
[ { "title": "周一的计划", "index": 1, "todoItems": [ { "index": 1, "time": "7:00 PM", "matters": "运动健身" }, { "index": 2, "time": "9:00 PM", "matters": "阅读" } ]
},
{
"title": "周二的计划", "index": 2, "todoItems": [
{ "index": 1, "time": "7:00 PM", "matters": "练习萨克斯" },
{ "index": 2, "time": "8:00 PM", "matters": "看一部电影" }
]
},
{
"title": "周三的计划", "index": 3, "todoItems": [
{ "index": 1, "time": "7:00 PM", "matters": "练习毛笔字" },
{ "index": 2, "time": "8:00 PM", "matters": "短距离夜骑" }
]
},
{
"title": "周四的计划", "index": 4, "todoItems": [
{ "index": 1, "time": "7:00 PM", "matters": "运动健身" },
{ "index": 2, "time": "8:00 PM", "matters": "高数学习" }
]
},
{
"title": "周五的计划", "index": 5, "todoItems": [
{ "index": 1, "time": "7:00 PM", "matters": "云顶之奕" }
]
},
{
"title": "周六的计划", "index": 6, "todoItems": [
{ "index": 1, "time": "7:00 AM", "matters": "绿道骑行" },
{ "index": 2, "time": "7:00 PM", "matters": "线代学习" }
]
},
{
"title": "周日的计划", "index": 7, "todoItems": [
{ "index": 1, "time": "8:00 AM", "matters": "学习,整理,总结" },
{ "index": 2, "time": "3:00 PM", "matters": "追番,追剧,追综艺" }
]
}
]
- 在 components 文件夹下创建 TodoItems.js 文件,WeekPlan.js 文件:
javascript
// TodoItems.js
import React from "react";
const TodoItems = ({ todoItems }) => (
<ul>
{ todoItems.map(item => (
/* 定义标签 class 属性时,JSX 中要用 className,因为 class 是 JavaScript 声明 类的保留字 */
<li clsssName="todo-item" key={item.index}>{ `${item.time}:${item.matters}` }</li>
)
)}
</ul>
);
export default TodoItems;
javascript
// WeekPlan.js
import React from "react";
import TodoItems from "./TodoItems";
const WeekPlan = ({ weekPlanList }) => (
weekPlanList.map(weekPlan => (
<React.Fragment key={weekPlan.index}>
<h2>{weekPlan.title.replace('的', '') + ':'}</h2>
<TodoItems {...weekPlan} />
</React.Fragment>
))
);
export default WeekPlan;
在组件中,导出用 export,导入组件用 import,这里我们可以拓展一下
-
export:主要用于暴露文件部分对象,且可以同时暴露多个。
-
export default:暴露文件内部所有对象。
-
import {} from "...": 接受 export 暴露的所有对象,接受对象的名称不能自定义,但可以接 as 来自定义对象名称。
-
import customName form "...": custTomName 可以为自定义名称。
-
来看个例子就很好理解了:
javascript
// exportExample.js
export const author = "export:炭烤小橙微辣";
const getAuthor = () => {
return `export default: ${author}`;
};
export default getAuthor;
javascript
import { author } from "./exportExample";
import getAuthor from "./exportExample";
import getAuthorByExample from "./exportExample";
import { author as authorAs } from "./exportExample";
import * as authorByAll from "./components/test";
console.log(author); // export:炭烤小橙微辣
console.log(getAuthor()); // export default:炭烤小橙微辣
console.log(getAuthorByExample()); // export default:炭烤小橙微辣
console.log(authorAs); // export:炭烤小橙微辣
console.log(authorByAll.author); // export:炭烤小橙微辣
module.exports 的导出与导入请看前边的文章《JavaScript 新特性》,这里就不再多赘述了。
- 在 src 目录下创建 index.html 文件:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>周计划</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
- 在 src 目录下创建 index.js 文件:
javascript
import React from "react";
import { createRoot } from "react-dom/client";
import WeekPlan from "./components/WeekPlan";
import planList from "./originalData/planList.json"
const root = createRoot(document.getElementById("root"));
root.render(<WeekPlan weekPlanList={planList} />);
把组件,数据文件都配置好了,就可以配置 webpack 打包的相关配置了。
我们安装 webpack,webpack-cli,在项目定义webpack版本,这样,webpack
css
npm install webpack webpack-cli --save-dev
局部安装:对项目定义 webpack 版本,打包的时候就不会出现包版本的问题了。
webpack-cli 是 webpack 的执行依赖,在 webpack-cli 中代码执行时,才是 webpack 进行编译和打包的过程。
那么 webpack 是怎样进行项目打包的呢?
当我们运行webpack时, webpack 会 查找当前目录下的 src/index.js 作为入口,然后从入口开始,会生成一个依赖关系图,这个关系图包含程序中所需的模块(js,css,图片等)然后历遍结构,打包一个个模块,其中会用到 loader 来解析不同文件来进行相应的解析。
我们知道 JSX 等文件需要编译才能被浏览器识别,这个时候有一个 叫做 Babel 的转换器,我们先安装 Babel 依赖:
sql
npm install babel-loader @babel/core --save-dev
安装 babel 预设:提前定义 bable 把 JSX,ESNext 语句转换成什么类型的语句。
sql
npm install @babel/preset-env @babel/preset-react --save-dev
我们在根目录新建一个 .babelrc 文件: 这个文件主要就是为 babel 提供编译规则,presets 数组中,越靠后的越先执行;
perl
// .babelrc
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
下边对这几个插件及依赖进行一个简单的介绍:
- babel-loader: 解析 ESNext,JSX 等语句的桥梁。
- @babel/core: 根据(.babelrc)中的规则,进行源码的转换。
- @babel/preset-env:提供 ESNext 转换为 ES5 的语法转换规则。
- @babel/preset-react: 对react语法的转换。
Babel 功能及其强大,对于 Babel 的详细介绍,感兴趣的 jym 可以看看 Babel的文档。
最后一步,安装插件 html-webpack-plugin, webpack-serve-dev
css
npm install html-webpack-plugin webpack-serve-dev --save-dev
webpack-serve-dev: 本地开发工具,本地可以直接启用服务来查看页面。
在使用 webpack 构建时,我们需要把 JSX 转换成 React 元素。需要新建一个配置文件: webpack.config.js(自定义打包配置文件), 这个文件名字固定,webpack 打包之前会解析这个文件,以获取打包的自定义配置。
javascript
// webpack.config.js
// path 为 Node.js 内置 npm 模块,只要为了更方便的处理文件或目录路径
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js", // 项目入口
devtool: "source-map", // 源码映射,方便使用源码调试
output: {
path: path.join(__dirname, "dist"), // 打包根目录下创建一个文件dist文件夹,里边是打包好的文件
filename: "bundle.js" // 所有的 react,JSX,EXNext 代码 都将被编译打包进这个文件
},
module: {
rules: [
// 在所有导入的除 node_modules 文件夹中的 JavaScript 文件上运行 babel-loader
{test: /.js$/, exclude: /node_modules/, loader: "babel-loader"}
]
},
plugins: [
new HtmlWebpackPlugin({
// 以public下index.html 为模版打包生成html文件, 打包的 js 文件 或其他 css 等文件,并在 这个文件中插入一个
// script 标签,标签的地址就是上边的 output.filename
template: path.resolve(__dirname, 'src/index.html')
})
]
}
最后配置 package.json 中的 scirpts 对象:
bash
{
"test": "echo "Error: no test specified" && exit 1",
"build": "webpack --mode production",
"dev": "webpack --mode develoment",
"start": "webpack-dev-server --open --mode development"
}
现在所有配置都已完成,现在想看到自己写的周计划项目的话,只需要在终端 输入命令:
arduino
npm run start
等服务启动成功,就可以在浏览器地址为 http://localhost:8080/
的页面看到自己写的周计划啦!
我们可以把项目打包到 dist 文件夹下,执行如下命令:
arduino
npm run build
经过打包过后,就多出来一个 dist 文件夹,里边的也就是打包过后的文件。
总结
我们这么大费周章的通过对 webpack,Babel 等工具的简单介绍,然后一步一步创建一个 React 应用,目的其实很简单,就是希望大家对从头创建一个 React 项目有所了解,虽然现在有更便捷创建 React 应用的方式,但是,这就是创建一个 React 项目的基础,当然,要创建一个应用级的项目,这些配置肯定是不够的,里边还有很多需要配置,比如 ESlint,router,state 等等,这也算是一个抛砖引玉的过程吧。
拓展: 作为一个 React 开发者, Create React App 这个工具能够帮助开发者快速创建 React 项目。 可以全局安装 Create React App包(不想安装:直接执行 npx create-react-app my-project):
lua
npm install create-react-app -g
安装成功后,就可以通过这个包来创建更加健全的 React 应用了。
lua
create-react-app my-project