1、npm run dev做了什么
答:在前端项目中,npm run dev并不是Node自带的命令,而是调用package.json中定义的scripts脚本,通常用来启动开发环境。它的具体行为取决于你使用的工具链(如Vite、Webpack、Next.js、React Scripts等)。
具体执行的事情:
- 启动开发服务器
- 本地开启一个
HTTP服务,通常监听localhost:3000或5173。 - 支持热更新
(HMR),修改代码后浏览器自动刷新或更新页面。
- 本地开启一个
- 构建/编译源代码
- 将
JSX/TS/ES6+代码编译成浏览器可执行的JS(Babel、esbuild 或 SWC)。 - 处理
CSS、Sass、Less、PostCSS、图片等资源。
- 将
- 生成模块依赖图
- 解析
import/export,打包模块,形成依赖关系树。
- 解析
- 启动热模块替换 (HMR)
- 监听源文件变化,修改对应模块时只更新变化部分,避免全量刷新。
- 提供
Source Map- 方便调试,让浏览器调试器能显示源码而不是打包后的代码。
npm run dev 是前端开发环境的启动命令,负责编译、打包、启动本地服务、提供热更新,让你可以在本地调试项目。
2、用到的打包工具有什么区别,为什么vite比webpack快
答:前端常用的打包工具有
- Webpack:老牌工具,功能最强大,生态丰富。采用基于配置的打包思路,所有文件先打包再交给浏览器。
- Rollup:偏向库打包(比如
Vue/React官方库都用它),支持Tree Shaking更彻底,但对多页应用支持一般。 - Parcel:零配置上手快,内置很多功能,但扩展性比
Webpack差。 - Vite:新一代构建工具,利用浏览器原生
ES Modules,开发环境快如闪电,生产模式下内部用Rollup打包。
Webpack和Vite 的区别在于:
- 开发环境的区别
- Webpack:
- 启动时需要把所有依赖文件打包好,再启动开发服务器。
- 项目大时启动慢(可能要十几秒甚至更久)。
- Vite:
- 不打包整个项目,而是直接利用 浏览器原生 ES Module
(<script type="module">)。 - 只有在页面请求某个模块时,才会用
esbuild(超快的 Go 语言编写的工具)做按需编译。 - 启动项目几乎是秒开。
- 不打包整个项目,而是直接利用 浏览器原生 ES Module
- 依赖预构建:
Vite启动时会用esbuild把依赖(如React、lodash等第三方包)转为高效的ES Module格式,之后直接走缓存。Webpack每次启动都要重新构建依赖,速度更慢。
HMR(热更新)的区别Webpack:更新某个文件后,要重新构建依赖图中相关的模块,更新速度会越来越慢。Vite:直接让浏览器重新请求被修改的模块,天然按需,更新速度恒定。
- 生产环境的区别
Vite开发环境用esbuild,生产环境依然用Rollup打包(保证稳定和优化能力)。Webpack一直用自己的打包逻辑。
- 适用场景
Webpack:适合大型企业项目,生态完善,插件丰富,复杂场景(比如微前端、定制 loader)依然强大。Vite:适合现代Web项目、组件库开发、快速迭代的应用。前端新项目大多首选Vite。
3、你能说说Vue3是如何追踪依赖并触发更新的吗?
答:Vue3的响应式机制是Proxy + effect 追踪依赖来实现自动更新。
- 响应式对象的创建,
Vue3使用reactive或ref创建响应式数据:reactive会返回一个Proxy,拦截对象的 读/写操作。ref是对基本类型值的包装,也会生成一个对象,内部通过getter/setter实现追踪。
- 依赖收集(依赖追踪)
Vue内部维护一个全局活动 effect(称为activeEffect)- 当
模板或computed、watchEffect中访问响应式数据时:Proxy的get拦截器被触发Vue会将当前activeEffect注册到这个属性对应的依赖集合(Dep)中
- 触发更新(响应式变化)
- 当你修改响应式数据时:
Proxy的set拦截器被触发Vue会找到 这个属性对应的依赖集合,将里面的effect都调度执行- 如果
effect是组件渲染函数,就会重新渲染组件
特性:
- 按需追踪:只追踪真正使用的数据,不使用的数据不会触发更新
- 嵌套响应式:
Proxy会递归创建深层对象响应式 - 自动清理:
effect执行前,Vue会清理之前依赖,避免重复收集
4、React中为什么useEffect有时会导致闭包问题?你是如何避免的?
问题:闭包捕获旧值
function Counter(){
const [count, setCount] = useState(0);
useEffect(()=>{
const id = setInterval(() => {
console.log(count); // ⚠️ 每次都是闭包里的旧 count
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
},[])
}
useEffect的回调函数在 初次渲染时就被创建- 闭包捕获了当时的
count = 0 - 所以
setInterval内的count 永远是 0 → 闭包陷阱 - 导致计数器不会累加如预期
useEffect闭包只会捕获它创建时的作用域变量- 如果依赖数组不包含某个
state/prop,闭包里的值就不会更新 - 这是
React的渲染和闭包机制导致的
解决办法:
- 把依赖加到依赖数组,但每次都会重新清除并创建新的
setInterval, - 使用函数式更新
setCount(prev => prev + 1);,setState 内部可以获取到最新的state,而不是闭包里的旧值