基于某公司面试项目复盘
你在MOM管理系统项目中使用了qiankun微前端框架,能否详细说明你是如何实现系统模块的拆分和独立开发的
什么是qiankun
- qiankun 是⼀个基于 single-spa 的微前端实现库,旨在帮助⼤家能更简单、⽆痛的构建⼀个⽣产可⽤微前端架构系统。微前端的核⼼⽬标是将巨⽯应⽤拆解成若⼲可以⾃治的松耦合微应⽤,⽽ qiankun 的诸多设计均是 秉持这⼀原则,如 HTML entry、沙箱、应⽤间通信等。这样才能确保微应⽤真正具备 独⽴开发、 独⽴运⾏的能⼒。
为什么要用qiankun
1. 在公司团队中每个人负责的模块不同。在项目初始阶阶。每个人后端都在写vue.因此会在有人漏传或写错代码。导致线上部署所有模块下导致报错
2. 在公司的经营理论上。我们是按模块去给甲方部署。如果不折分。一套代码都需要部署在一个项目上
3. 项目模块越积越大。在开发模式中需要跑的时间很慢
4. 不同的技术想要入到这套系统中来
5. 让业务组的同事开发最纯粹的业务,⽽不⽤关⼼UI组件的事情
6.请求方法和公共组件引用混乱和重复,比如有的人用axios。各自的前缀不一样。
如何实现系统折分
- 提取项目公共组件库和公共方法。去建立组件库npm 。比如我们项目的单据。列表,工具栏。搜索栏
- 模块折分子工程。根据现在的功能让项目负责的人去收集哪些是wms模块。哪些是MES的模块。并加入子应用的配置json
- 折分后子工程的打包编译webpack打包 output—加前缀子产品在dist各自文件夹
- 针对请求的混乱vue.config去实现prxyo代码的重写,对常用后缀进行重新梳理定向
- 路由加入模块的前缀。在打开子工程时在qiankun mount的周期将对应子模块路由注册过去
- 创建自定义npm命令。方便启动开发环境。在启动时。会获取当前目录下的文件
你了解qiankun的js沙箱机制吗?
快照沙箱(Snapshot Sandbox)
工作原理: 快照沙箱通过在子应用挂载前保存全局状态(即 window
对象的所有属性),在子应用卸载时恢复全局状态,从而实现状态隔离。
实现步骤:
- 挂载前保存快照: 在子应用挂载前,将当前全局状态保存到一个快照对象中。
- 子应用运行期间: 子应用可以自由修改全局状态。
- 卸载后恢复快照: 在子应用卸载时,将全局状态恢复到挂载前保存的快照状态。
优点:
- 简单直接,容易实现。
- 不依赖现代 JavaScript 特性,兼容性好。
缺点:
- 每次挂载和卸载都需要保存和恢复全局状态,性能开销较大。
- 不适用于高频率的子应用切换。
代理沙箱(Proxy Sandbox)
工作原理: 代理沙箱通过 JavaScript 的 Proxy
对象拦截对子应用的全局变量访问和修改,从而实现沙箱隔离。
实现步骤:
- 创建
Proxy
对象: 创建一个Proxy
对象,代理window
全局对象。 - 拦截全局对象操作: 通过
Proxy
的get
、set
等拦截器方法,拦截对子应用全局变量的访问和修改。 - 运行子应用: 将子应用的全局变量操作都定向到
Proxy
对象上。 - 子应用销毁时: 清理
Proxy
对象的状态,不影响主应用和其他子应用。
优点:
- 性能更优,因为无需频繁保存和恢复全局状态。
- 更灵活,可以对全局对象的操作进行精细的控制和过滤。
缺点:
- 依赖现代 JavaScript 特性(如
Proxy
),可能在一些旧环境中不兼容。 - 实现相对复杂,需要处理更多的细节。
如何实现css隔离
在这个项目中,你提到你负责了组件库的二次封装,能否举例说明一个具体的组件,并描述你如何进行封装和优化?
以公司的单据组件为例,单据分为三个模块。工具栏。表头。表体
- 它接收了许多参数。主要有Head,Body的参数。Rule规则。事件监听参数(比如工具栏的事件。表头表体的输入监听),是否可编辑
- 暴露了单据的调用。比如获取模板或添加行或插入行的方法
- 结合前后端对修改。编辑,新增作下标索引,在传给后台方便后台处理
- 分组单据结构需要结合模板分组。与获取单据的数据一致
遇到的问题:单据表格多数据的情况下会导致卡,
- 在过往老代码结构在新增或修改按钮触发时强行用key来更新数据的状态
- 表格采取的是开启编辑模式下,对自定义的输入组件全部渲染,后面采取行编辑。只有在点击行的时候 才能显示渲染的输入组件
如何知道他们卡。这里我们用
使用了Ant Design的G6图形库来开发车间看板的配料流程图,请具体讲一下你是如何实现的,包括遇到的挑战和解决方案。
遇到的问题:原本流程的G6图是用vue构建的独立页面。在电脑显示没问题。在平板中无法显示出来,平板是安卓9
在排查过程中发现是js的flat语法不兼容。然后用webpack的 babel-preset-env进化打包转义es5 同时添加入口文件添加import'core-js/stable';import'regenerator-runtime/runtime'; 。这会根据 browserslist 目标导入所有 polyfil,这样你就不用再担心依赖的 polyfi 问题了,但是因为包含了一些没有用到的 polyfi 所以最终的包大小可能会增加。
系统你有开发了水印,请问遇到了什么问题和难点
水印的换行高度问题
水印问题换行:在写死宽度的情况下。在用canva时绘制时可以利用canvs. wrapText来处理。 但是这里有个问题。因为你的文字是用transfrom斜边角度。如何计算这个高度呢。这个时候就拿三角函数来处理这个问题。比如我是tan30度 = 对边的高度/邻边的长度。然后领边的长度就是width 。高度就是我要求的值,在获取高度后去绘一张图片出来。然后用BackgrounImag去重复图片
水印的F12问题
要处理这个问题,要MutationObserver这个方法。Mutation Observer是用于代替Mutation events作为观察DOM树结构发生变化时,做出相应处理的方法,这个api方法是元素在增册改过程中都会触发到。
前端导出excel表头分组
思路是什么
-
先判断是否分组字段存在。不存在则提示异常
-
先遍历分组如果一级分组标题相同,则抛出异常。因为用一级分组作为key存诸,后续
-
获取/所有列,所有列的key,/总共所有组的最大层级,
-
遍历分组。遍历分组的列并且处理好数据格式存诸起来。并且计算每组的层级
-
获取最高层级添加excel的行数,用行数代替列
-
处理分组的列,我们通过传的分组进行单元格的合并,这里的处理用层序遍历处理
{
title: '生产成品质量情况', //分组标题
column: [
'ProdQC_Appear',
'ProdQC_Stick',
'ProdQC_Long',
'ProdQC_Wide',
'ProdQC_Weight',
'ProdQC_IsRoll',
],
children: [
{
title: '厚度',
column: ['ProdQC_Thick_Left', 'ProdQC_Thick_Center', 'ProdQC_Thick_Right'],
},
],
}
说下React Diff
在传统的diff算法
中复杂度会达到O(n^3)
。React
中定义了三种策略,在对比时,根据策略只需遍历一次树就可以完成对比,将复杂度降到了O(n)
:
- tree diff 在俩个树对比中。只会比较同一层的节点。会忽略跨层级的操作
- components diff 在对比组件中。会判断是否相同。如果不同,则会替换整个组件的节点
- element的对比。对于同一层级的一组节点。会使用具有唯一性key来区分是否需要创建。册除或移动
Recat的合成事件与原生事件的区别
react基于浏览器的事件机制自身实现了一套事件机制。包括事件注册。事件的合成。事件的冒泡。事件的派发。这套事件机制被称为合成事件
React 中的合成事件有两种绑定方式:驼峰形式书写事件属性名或 props 值为函数的指针而非字符串
实现机制
在 React 底层,主要对合成事件做了两件事:事件委派 和 自动绑定
事件委派: React 使用事件委派来提高性能。它通过在文档的高层级元素(通常是 document)上添加单个事件监听器来处理所有相应类型的事件。 当事件发生时,React 会使用事件冒泡机制来捕获并处理事件。这意味着无论事件在哪个子元素上触发,都会冒泡到高层级元素,并由 React 的事件监听器处理。 这种方法减少了必须直接附加到元素上的事件监听器的数量,从而减少了内存占用并提高了性能
自动绑定: 在类组件中,React 通过 bind 方法自动绑定事件处理器,确保 this 关键字在回调函数中指向正确的上下文。在创建事件处理器时,你不需要手动绑定 this。React 会自动处理这个过程,使得事件处理器中的 this 指向当前组件的实例
事件冒泡的执行顺序
原生事件:子元素 DOM 事件监听!
原生事件:父元素 DOM 事件监听!
React 事件:子元素事件监听!
React 事件:父元素事件监听!
原生事件:document DOM 事件监听!
React中的key有什么作用?
- key 应该是唯一的
- key不要使用随机值(随机数在下一次 render 时,会重新生成一个数字)
- 使用 index 作为 key值,对性能没有优化
react的生命周期
-
创建阶段:
constructor
、getDerivedStateFromProps
、render
、componentDidMount
-
更新阶段:
getDerivedStateFromProps
、shouldComponentUpdate
、render
、getSnapshotBeforeUpdate
、componentDidUpdate
-
卸载阶段:
componentWillUnmount
创建阶段
当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
constructor()
: 在 React 组件挂载之前,会调用它的构造函数。getDerivedStateFromProps()
: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。render()
: render() 方法是 class 组件中唯一必须实现的方法。componentDidMount()
: 在组件挂载后(插入 DOM 树中)立即调用。
更新阶段
getDerivedStateFromProps()
: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。shouldComponentUpdate()
:当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。render()
: render() 方法是 class 组件中唯一必须实现的方法。getSnapshotBeforeUpdate()
: 在最近一次渲染输出(提交到 DOM 节点)之前调用。componentDidUpdate()
: 在更新后会被立即调用。
卸载
componentWillUnmount()
: 在组件卸载及销毁之前直接调用。
useEffect如何模拟三个生命周期
在 React 中,useEffect
钩子可以模拟三个生命周期方法:componentDidMount
、componentDidUpdate
和 componentWillUnmount
-
模拟
componentDidMount
:useEffect
的第二个参数是一个依赖数组。如果将其设置为空数组[]
,则useEffect
只会在组件挂载时执行一次,类似于componentDidMount
。- 在这个空数组的
useEffect
中,您可以执行一些只需要在组件挂载时运行一次的操作。
-
模拟
componentDidUpdate
:- 如果在依赖数组中指定了某个变量,那么
useEffect
将在该变量发生变化时执行。 - 这类似于
componentDidUpdate
,因为它会在组件更新时运行。
- 如果在依赖数组中指定了某个变量,那么
-
模拟
componentWillUnmount
:- 在
useEffect
中返回一个清理函数,它会在组件卸载时执行。 - 这类似于
componentWillUnmount
,可以用于取消订阅、清除定时器、移除事件监听器等。
- 在
说说对Fiber架构的理解
react native和react有什么不同。生命周期是否相同
,React Native 确实比react相同。但也包括了一些特定于移动应用的生命周期事件,这些事件与应用的状态变化和用户交互有关。以下是一些 React Native 中常见的特定于移动应用的生命周期方法:
-
componentWillUnmount()
:- 在组件被卸载(从 DOM 中移除)之前调用。
- 通常用于清理资源、取消订阅、关闭连接等操作。
- 例如,在一个聊天应用中,你可以在此方法中取消 WebSocket 的订阅。
-
componentDidFocus()
和componentDidBlur()
:- 这些方法在应用进入前台或后台时被调用。
componentDidFocus()
在组件获得焦点时调用,例如当用户从其他应用切换回你的应用时。componentDidBlur()
在组件失去焦点时调用,例如当用户切换到其他应用时。- 你可以在这些方法中执行一些与应用状态相关的操作,例如暂停和恢复动画、保存用户输入等。
-
AppState
模块:- React Native 提供了
AppState
模块,用于监听应用的状态变化。 - 你可以通过监听
change
事件来处理应用进入前台或后台的情况。 - 例如,你可以在应用进入后台时保存用户数据,以便在下次进入应用时恢复状态。
- React Native 提供了
总之,React Native 的生命周期方法不仅包括了 React 的标准生命周期,还添加了一些与移动应用相关的方法,以便处理应用状态、用户交互和后台/前台切换等场景。
React APP横屏锁定
在android\app\src\main\AndroidManifest.xml 中设置 android:screenOrientation属性的值来实现横屏(landscape)、竖屏(portrait)锁定,
在处理某个工艺制程,加工工序的列表有个需要个距离你现在开始点击工序后。需要计算你点击开始工序的时间到你现在的时间是多少时间。并且每隔5s更新一次时间
初始方案
定义个时间组件,接收创建时间。在组件销毁时停止计时,遇到多数据工序需要这样的时候就会有多个计时器
优化方案如下
定义一个时间组件,定义二个参数,refleshTime[更新],time[创建时间] 从父组件的的定义个变量用来触发refleshTime,用useEffect这样来触发重新计算时间的函数
useEffect(() => {
const calculateAndSetTimeDiff = () => {
const diff = calculateTimeDifference(time); // 计算时间差
setTimeDiff(diff);
};
calculateAndSetTimeDiff();
return () =>{
};
}, [time,StartTime]);
说下 react native的桥接
React Native 的桥接(Bridge)机制是其核心特性之一,它允许 JavaScript 代码与原生代码进行通信。这个桥接机制是 React Native 能够实现跨平台功能的关键。
桥接的工作原理如下:
- JavaScript 线程:在 React Native 中,JavaScript 代码运行在一个单独的线程中,负责处理业务逻辑和用户界面。
- 原生模块:原生模块是用 Objective-C 或 Java 编写的,可以访问设备的 API 和功能。
- 消息传递:当 JavaScript 代码需要调用原生模块的功能时,它会通过桥接发送一个消息给原生模块。原生模块执行操作后,再将结果通过桥接发送回 JavaScript 线程。
- 事件处理:原生事件(如用户输入、计时器、网络请求等)首先在原生部分注册并产生数据,然后通过桥接层传给 JavaScript 部分。JavaScript 层处理数据后生成一系列指令,再通过桥接层传回原生部分,原生端根据这些指令调用相应的原生模块并更新 UI。
- TurboModules 和 JSI:React Native 正在向新的桥接系统 TurboModules 过渡,它提供了更高效和可靠的通信机制。TurboModules 使用 C++ 代码生成和静态类型检查,以提高性能和稳定性。JavaScript Interface (JSI) 允许 JavaScript 代码直接与原生代码进行交互,而无需通过传统的桥接。
react native model全屏问题
发现弹出modal时,无论怎么设置StatusBar,都无法有效隐藏掉状态栏。搜索了一圈,发现了个解决办法:直接设置Theme即可:编辑android/app/src/main/res/values/styles.xml:
<style name="AppTheme" parent="Theme.ReactNative.AppCompat.Light.NoActionBar.FullScreen">
<!-- Customize your theme here. -->
</style>
react native的打包环境变量,包名,版本
react native对环境变量官网基本没有方案,引用第三方的包react-native-config.去处理 添加.env.development 添加.env.production
在app/build.gradle去定义函数
// 定义一个函数来加载和解析 package.json
def loadPackageJson() {
// 使用 projectDir 获取项目根目录,然后 parentFile 来获取上级目录
def packageJsonFile = new File(project.projectDir.parentFile.parentFile, 'package.json')
if (!packageJsonFile.exists()) {
throw new FileNotFoundException("Cannot find package.json at ${packageJsonFile.path}")
}
def jsonSlurper = new JsonSlurper()
def packageJson = jsonSlurper.parse(packageJsonFile)
return packageJson
}
def packageJson = loadPackageJson();//获取packageJson的对象
websoct是什么
WebSocket 是一种协议,用于在 Web 应用程序中创建实时、双向的通信通道。
传统的 HTTP 请求通常是一次请求一次响应的,而 WebSocket 则可以建立一个持久连接,允许服务器即时向客户端推送数据,同时也可以接收客户端发送的数据。WebSocket 相比于传统的轮询或长轮询方式,能够显著减少网络流量和延迟,提高数据传输的效率和速度。它对实时 Web 应用程序和在线游戏的开发非常有用。
WebSocket 可以在浏览器和服务器之间建立一条双向通信的通道,实现服务器主动向浏览器推送消息,而无需浏览器向服务器不断发送请求。其原理是在浏览器和服务器之间建立一个“套接字”,通过“握手”的方式进行数据传输。由于该协议需要浏览器和服务器都支持,因此需要在应用程序中对其进行判断和处理
websoct的原理是什么
WebSocket 是 HTML5 开始推出的基于 TCP 协议的双向通信协议,其优势在于与 HTTP 协议兼容、开销小、通信高效。WebSocket 让客户端和服务器之间建立连接,并通过这个持久连接实时地进行双向数据传输。
其实 WebSocket 最主要的特点就是建立了一个可持久化的 TCP 连接,这个连接会一直保留,直到客户端或者服务器发起中断请求为止。WebSocket 通过 HTTP/1.1 协议中的 Upgrade 头信息来告诉服务器,希望协议从 HTTP/1.1 升级到 WebSocket 协议。
WebSocket 建立在 HTTP 协议之上,所有的 WebSocket 请求都会通过普通的 HTTP 协议发送出去,然后在服务器端根据 HTTP 协议识别特定的头信息 Upgrade,服务端也会判断请求信息中 Upgrade 是否存在。 这里面 HTTP 是必不可少的,不然 WebSocket 根本无法建立。特别的,WebSocket 在握手时采用了 Sec-WebSocket-Key 加密处理,并采用 SHA-1 签名。
一旦建立了 WebSocket 连接,客户端和服务器端就可以互相发送二进制流或 Unicode 字符串。所有的数据都是经过 mask 处理过的,mask 的值是由服务器端随机生成的。在数据进行发送之前,必须先进行 mask 处理,这样可以有效防止数据被第三方恶意篡改。
最后需要说明一下的是,WebSocket 的通信协议是基于帧(数据包)的。在数据发送时,一个完整的数据包可以分为多个帧进行发送,而每一个帧都包含了数据的一部分,同时还包含了帧头信息。
WebSocket 和 HTTP 的关系?
两种协议都是用于网络通信的。
- HTTP (Hypertext Transfer Protocol) 是一个基于请求和响应模式的协议,最早用于 Web 应用。
- WebSocket 是一种双向通信协议,可以在客户端和服务器之间建立持久的连接,以实现实时通信。
- WebSocket 协议在建立连接时需要使用 HTTP 协议。 具体来说,当客户端想要建立 WebSocket 连接时,它们需要通过 HTTP 请求发送一个握手请求。如果服务器同意握手,它将发送一个握手响应,HTTP 协议随后会升级到 WebSocket 协议。
- HTTP 和 WebSocket 协议在用途上也有所不同。HTTP 协议主要用于客户端和服务器之间的请求和响应通信,而 WebSocket 协议主要用于实现实时通信和服务器推送。通俗的说,HTTP 协议是“一问一答”,WebSocket 协议是“对话”。
总之,WebSocket 和 HTTP 是两种不同的协议,它们在通信模式、用途和建立连接时都有所不同,但是它们之间也有联系。
你说你有用到websocket通信。你如何用。难点在哪,主要解决是车间因网络不好。websoct需要重新连接,同时有时候还要处理心跳监测
//ws的api非常简单
// 创建ws连接
const ws = new WebSocket('ws://localhost:8080/test');
ws.onopen = function() {
console.log('WebSocket 连接已经建立。');
ws.send('Hello, server!');
};
ws.onmessage = function(event) {
console.log('收到服务器消息:', event.data);
};
ws.onerror = function(event) {
console.error('WebSocket 连接出现错误:', event);
};
ws.onclose = function() {
console.log('WebSocket 连接已经关闭。');
}
断网重连的思路
- 记录重连次数:通过 reconnectAttempts 属性记录当前已经尝试重连的次数。
- 设置最大重连次数:通过 maxReconnectAttempts 属性设置允许的最大重连次数。
- 重连逻辑:在 onclose 和 onerror 事件中调用重连处理函数 handleReconnect。
- 重连间隔:通过 reconnectInterval 属性设置每次重连的间隔时间,可以在每次重连时增加间隔以实现指数退避。
心跳监测的思路
实现自动心跳的基本思路
- 发送心跳消息:在 WebSocket 连接建立后,启动一个定时器,定期发送心跳消息到服务器。
- 接收心跳响应:服务器收到心跳消息后返回响应,客户端接收到响应后重置定时器。
- 检测心跳超时:如果在指定时间内没有收到心跳响应,则认为连接断开,进行重连。
说下你对redux的了解
redux将所有的状态进行集中管理,当需要更新状态的时候,仅需要对这个管理集中处理,而不用去关心状态是如何分发到每一个组件内部的
redux
就是一个实现上述集中管理的容器,遵循三大基本原则:
-
单一数据源:
- 整个应用的状态存储在一个对象树中,并且这个对象树只存在于唯一的一个 store 中。
-
状态是只读的:
- 唯一改变状态的方法是触发 action,action 是一个描述发生事件的普通对象。
-
使用纯函数来执行修改:
- 为了指定状态树如何由 actions 转换,你需要编写 reducers。
- Reducer 是一个纯函数,它接收先前的状态和一个 action,并返回新的状态。
工作原理:
- 当应用中发生了某个事件(如用户操作),你会分派一个 action。
- 这个 action 会被发送到 store,store 会将当前的状态和这个 action 一起传递给 reducer。
- Reducer 会根据 action 类型和传递的信息来更新状态,然后返回一个新的状态对象。
在redux-thunk中, 如果通过dispatch派发的任务是一个对象, 那么就立即执行reducer 如果通过dispatch派发的任务是一个函数, 那么就执行这个函数
简单说下promse
Promise 是 JavaScript 中用于处理异步操作的对象。它代表了一个尚未完成,但预期将来会完成的操作的最终结果。Promise 对象可以处于以下三种状态之一:
- 待定(Pending) :初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(Fulfilled) :意味着操作成功完成。
- 已拒绝(Rejected) :意味着操作失败。
当创建一个 Promise 时,您需要提供一个执行器函数,这个函数接受两个参数:resolve
和 reject
。当异步操作成功时,您调用 resolve
并传递结果;如果操作失败,您调用 reject
并传递错误信息。
处理多个 Promise 的执行结果通常使用 Promise.all
方法。这个方法接受一个 Promise 数组作为输入,并返回一个新的 Promise。这个新的 Promise 仅在所有输入的 Promise 都成功完成时才会成功完成,返回值是一个数组,包含了所有输入 Promise 的结果。如果任何一个输入的 Promise 失败,Promise.all
返回的 Promise 就会立即失败,返回那个导致失败的错误信息
async 的核心是什么
async
和 await
的核心原理是基于 JavaScript 的 Promise 和 Generator 函数。这两个关键字使得异步代码的编写和阅读更加直观和简洁,类似于同步代码的风格。
-
async
关键字:-
用于声明一个异步函数,它会隐式地将函数的返回值封装成一个
Promise
对象。 -
这意味着
async
函数总是返回一个Promise
,即使函数体内没有显式返回Promise
。 -
例如:
async function example() { return 'Hello, World!'; } // 调用 example 函数会返回一个解析为 'Hello, World!' 的 Promise 对象
AI 生成的代码。仔细查看和使用。 有关常见问题解答的详细信息.
-
-
await
关键字:- 用于等待一个
Promise
完成并获取其结果值。 - 它只能在
async
函数内部使用,或者在模块的顶层代码中。 - 当
await
遇到一个Promise
,它会暂停函数的执行,直到Promise
解决(fulfilled)或拒绝(rejected)。 - 在
Promise
解决后,函数会恢复执行,并且await
表达式的结果是Promise
解决的值。 - 如果
await
后面跟的不是Promise
,它会被转换成一个立即解决的Promise
。
- 用于等待一个
webpack打包 plugin能代替loader的功能吗
在Webpack中,plugin和loader有不同的功能和用途。Loader主要用于在打包过程中的文件级别上转换文件,例如将TypeScript转换为JavaScript或加载CSS文件。而Plugin则在打包或块级别上工作,通常在打包生成过程的末尾执行。Plugin可以修改打包本身的创建方式,因此它们比loader拥有更强大的控制能力
简而言之,loader用于处理单个文件的转换,而plugin可以影响整个构建过程,包括优化、资源管理和环境变量注入等。因此,plugin不能完全代替loader的功能,它们各自有特定的角色和用途。如果你需要对文件进行预处理或转换,你应该使用loader。如果你需要更广泛地控制Webpack的构建过程,那么使用plugin会更合适
如何用命令行去处理qiankun子应用和基座的启动
如何从0到1用webpack搭建脚手架项目
-
初始化项目:
- 首先,创建一个新的项目目录,例如
webpack-demo
。 - 在该目录下运行
npm init
命令,生成一个package.json
文件。
- 首先,创建一个新的项目目录,例如
-
安装Webpack和相关依赖:
-
使用以下命令安装Webpack及其相关依赖:
npm install webpack webpack-cli --save-dev
-
-
创建Webpack配置文件:
-
在项目根目录下创建一个
webpack.config.js
文件,用于配置Webpack。 -
在配置文件中,设置入口文件、输出目录等基本配置,例如:
JavaScript
const path = require('path'); module.exports = { mode: 'development', // 开发模式 entry: path.resolve(__dirname, 'src/main.js'), // 入口文件 output: { filename: '[name].[hash:8].js', // 输出文件名 path: path.resolve(__dirname, 'dist'), // 输出目录 clean: true, // 打包前清空输出目录(Webpack 5新增) }, };
-
-
安装并配置HTML插件:
-
安装
html-webpack-plugin
插件,用于自动生成HTML文件并引入打包后的JS文件:npm install html-webpack-plugin --save-dev
在Webpack配置文件中添加以下代码:
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// ...其他配置
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/index.html'), // 使用指定的HTML模板
}),
],
};
- 处理CSS文件:
-
安装相关loader来解析CSS文件:
npm install style-loader css-loader less less-loader --save-dev
-
在Webpack配置文件中添加以下规则:
module.exports = { // ...其他配置 module: { rules: [ { test: /.css$/, use: ['style-loader', 'css-loader'], }, { test: /.less$/, use: ['style-loader', 'css-loader', 'less-loader'], }, ], }, };
- 为CSS添加浏览器前缀:
- 安装 `postcss-loader` 和 `autoprefixer` 插件:
```
npm install postcss-loader autoprefixer --save-dev
```
- 在Webpack配置文件中配置 `postcss-loader`:
JavaScript
```
module.exports = {
// ...其他配置
module: {
rules: [
{
test: /.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader'],
},
{
test: /.less$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'],
},
],
},
};
```
7. 运行项目:
-
在
package.json
中添加一个打包命令:"scripts": { "build": "webpack --config webpack.config.js" }
转载自:https://juejin.cn/post/7375345026429255691