【vue:把一个JS项目改成vue框架】forkify项目(一)

💡开始之前:要求至少已经学过JS和Vue,如果没有学过,本人墙裂推荐Udemy的课程,b站可看,可以边看JS课程,边实现以下。

我总共记录了三篇博客,这是第一篇,我最终的代码已经放在GitHubhttps://github.com/yudengisemily/forkify-from-js-to-vue.git,可以相互学习,转载请注明出处,下面我们就开始吧!😉

实践目标:把一个JS项目改成vue框架
JS项目:udemy排名第一的JS课程(下)的forkify
代码:https://github.com/jonasschmedtmann/complete-javascript-course.git
final:
在这里插入图片描述
start:
在这里插入图片描述

1. forkify项目(一)

1. 首先初始化vue项目

在这里插入图片描述
在这里插入图片描述


在这里插入图片描述

//main.js
import { createApp } from 'vue'
import App from './App.vue'
import './sass/main.scss'

const app = createApp(App);

app.mount('#app')

试一下是否移植成功:在App.vue中,

...
<button @click="test"></button>
...
export default{
	....
	methods:{
		test(){console.log("test")}
	}
}

2. 根据布局(页面)分裂出不同的component

在这里插入图片描述
分别对应html中的
在这里插入图片描述

ps:因为是这个项目,它没有页面的切换,所以可能用不到routing。
一般有页面切换的项目,会重新建一个page文件夹。
那么我在这里建一个layout文件夹,可能更符合这个项目。

接下来,根据布局分裂出3个的component:
在这里插入图片描述


  • 自此开始,在vue框架下根据需求逐一写(复制)出component的功能

3. ⚡需求一:从API请求数据并渲染

这个需求并不属于任何 layout,是后台的功能,所以写在App里,并且数据保存在App里,也更利于向全局provide或props数据。

项目中提供的api:https://forkify-api.herokuapp.com/v2(要科学调用)

在这里插入图片描述
得到数据后,要渲染数据,就是说把数据传递到recipe组件中,在recipe这个模块下动态展示数据
在这里插入图片描述
在这里插入图片描述
接下来在<template>中渲染,替换变量,


ps:以往JS这里是采用的模板字符串(Template String),如:

`string text ${expression} string text`

ps:在此之前有个小问题:在app里添加created
在这里插入图片描述


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
到这,就只剩ingredients变量没有替换了,以往JS需要将这段insertHtml,但是vue通过组件实现。

接下来是替换ingredients


以往JS是这样:
在这里插入图片描述
对recipe的每一个ingredient循环,返回一个template string,再将所有的ingredient html 合并用join

可以看出,JS写的,ingredient之前的图标此时都还为设置,它需要import icons,而vue到此就可以直接更新
在这里插入图片描述


在这里插入图片描述
在这里插入图片描述


在这里插入图片描述

当然,这种分块的html元素确实可以再建一个component来管理,比如recipeItem什么的,之后再说。


然后,整理一下,把一些test 的button删掉,到此这样,recipe布局完成:
在这里插入图片描述

4. ⚡需求二:在等待数据时有一个spinner

spinner是在数据到来之前,所以肯定是放在数据所放的地方,App.vue中。
spinner是很常用的功能,所以应该重新建立一个组件,管理spinner,这样就不需要多次写spinner的html代码了。


在JS中:
在这里插入图片描述


在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
现在已经完成了渲染某一个数据,接下来就是实现调取一系列数据,渲染一系列选项,然后通过左边的list选择食谱,在recipe组件里渲染。

5. ⚡需求三:监听路径id渲染页面

选择具体某食谱,url就会改变,这里可以用到router。
但是,也由于这个项目的特殊性,它不是通过url跳转到某一个页面,而是所有操作都只在这一个页面中进行,通过url的改变执行某个函数;
它有的是不同的布局,不同的渲染部分,所以router这里只是通过url获取到id,再监听id,和JS做的事一样,并没有通过router来重定向到别的组件。
在这里插入图片描述
在这里插入图片描述


在JS中:
在这里插入图片描述

在这里插入图片描述


在Vue中也可以使用传统的window.location实现如下:
在这里插入图片描述


轻微使用了router:
在这里插入图片描述
在这里插入图片描述
这里有一个待解决的bug:就是访问this.$route.params时是{ },也就是说没有检测到 id 属性。
在这里插入图片描述

6. 项目构思框架对比

JS采用的框架是:
在这里插入图片描述

这样做的好处是后台请求数据和前端显示数据完全分离,他们不直接接触,而是通过一个controller来渲染,引用原话:The model and the view are completely stand alone and completely isolated, they don’t import each other, they don’t even import the controller, in fact, they don’t even know the controller exists. All they do is basicly just sit there waiting to get some instructions from the controller.


Vue采用的框架通常是:

  • 首先,确定好page,(page文件夹里有几个vue文件),使用router管理
  • 其次,page会调用各种组件(组件统一存放在components文件夹,里面有vue文件)
  • 此外,components里面除了page使用到的组件,还会包括一些与ui有关的(BaseCard.vue、BaseButton.vue)和与layout有关的(TheHeader.vue)组件
  • 最后,为了方便各组件调用的数据是同步更新后的,状态管理由vuex完成:建立store文件夹和index.js文件管理modules,安装并引入vuex,在store中又会有不同的modules,modules中包含index.js、getters.js、mutations.js、actions.js

7. git 版本控制

由于接下来对原来的代码将改动很大,并且也并不清楚是否能完成需求四,所以可以在此时保存一个代码版本,我将使用 git
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

会变成
在这里插入图片描述
在这里插入图片描述
假如修改了代码,想要返回上一个版本,只需要git reset --hard HEAD
在这里插入图片描述
在这里插入图片描述


  • 方式一:

git log 可以查看所有发布的版本,
复制版本的id号,
q退出。
在这里插入图片描述
git reset --hard 上面的id


  • 方式二:
    git branch branch_name 创建分支,
    git checkout branch_name 切换到分支,
    git add -A 添加修改过的代码,
    git commit -m 'message_you_want_to_convey' 提交,
    git branch 查看分支,
    q退出。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

git checkout master 切换回主枝

在这里插入图片描述

8. ⚡需求四:状态管理数据(vuex)

不论是从 api 调过来的数据还是用户返回的数据,都应该统一管理,这样就不需要在每个调用数据的组件中更新数据的状态,所有人(组件)收到的数据都是动态变化的。
那么,对于这个项目,我们应该把获取到的recipe存在状态中进行管理。首先确保npm install vuex
在这里插入图片描述
main.js里面导入的store,要从index.js里面导出store,
在这里插入图片描述
同理,从index.js里导入recipeModule,也要在index.js里有相应导出,
在这里插入图片描述
这个index.js里面就应该存放我们的recipe数据。


在状态管理中,每一个js文件各司其职,不要混搭。比如mutation必须是同步的,所以不要在mutation中执行异步操作。对于异步操作,应该使用action。 因为,如有多个mutation在执行的时候,必须确保他们得到的state是实时同步的,如果有一个mutation在异步改变状态,那么其他mutation得到的状态就不是最终实时的状态(latest state),这会带来错误。

  • action就是用来请求数据或定义数据,然后提交commit给mutation来处理
  • mutation就是用来改变数据,
  • getters就是用来获取数据,全是return
  • index就是用来存放数据,并把上面所有的一切export到modules外面。

推荐课程,【Udemy排名第一的Vue3课程】2022 Vue3 - 完整开发指南(incl. Router & Composition API)(中英文字幕)下
P201,讲得很清楚,为什么要用mutation:是为了让外界以统一的方式在改变state,而不是直接对this.$store.state.xxxx变量进行操作。


接着上方,创建好框架之后几乎就是复制粘贴了,(记得在分支中进行操作git checkout withvuex,我的分支名称为withvuex

首先这个index.js里面就应该存放我们的recipe数据:

在这里插入图片描述
既然引入了mutation、actions、getters,那么他们都必须要导出对象,即export default {} ,可以先写上,避免报错。

现在我们一一试着设置这三个文件。明白内在逻辑就不难,首先数据的请求要放在action中,在mutation中改变(赋值)recipe,然后外部通过getters获取recipe。

首先把App.vue中对数据的操作移植到action中。

复制:
在这里插入图片描述
粘贴到action
在这里插入图片描述
在这里插入图片描述
context.commit('addRecipe',Recipe)

既然action的结果要提交给mutation,那么mutation中就要把获得的payload的值更新到state中去,
在这里插入图片描述
接下来就是调用的问题,怎么样才能让外部调用到action中的showRecipe() 函数,从而引发recipe的改变?
又要怎么样才能让外部获取到新的recipe?
在这里插入图片描述
首先是在这两个位置去思考,
vuex设置的是通过一个dispatch来调用action中的操作,通过getters来获取state的实时值(当然也可以直接this.$store.state.recipe ,但不建议这么做,getters会显得很没有面子🙃)

data中有两个数据,他们变成state,所以getters肯定也要设置这两个值的获取方式,所以先写好getters.js
在这里插入图片描述
在这里插入图片描述

注意了,由于已经设置了namespace,所以必须通过模块名字才能获取到state,如果是this.$store.getters['getRecipe'] 将获取不到state,同理对dispatch 。


好了,接下来,基本能把状态管理完成,但是在页面更新时,出现一个问题,如下:(
ps:console.log('1')在actions中,console.log('2')在getters中)
在这里插入图片描述
出现一个一直在加载的情况,说明isLoading并没有获取到最新的值,而一直保持true,并且,从打印顺序看出,action是最后完成的,还没等action中请求完数据,这个created(){}里面的console.log就都执行完了,所以这个action中的函数,看似在前面,实际是异步的。下面给出他们的完成顺序:4123
在这里插入图片描述
当我重新加载一下(比如随便去掉分号)这个App.vue组件(因为我想让created重新执行一遍),此时就能够得到recipe,并能正常渲染,这是为什么呢?
在这里插入图片描述

因为此时,上一次的action中已经请求到了数据,并且state已经更改,这样data中通过getter获取state时,就已经得到了相应的数据,就能够渲染。

  • 综上,问题就出在App.vue中,数据没有根据改变而自动更新,且没有仅更新数据,而不是通过重新加载页面来更新
  • 解决:computed:{}
    在这里插入图片描述
    在这里插入图片描述
    由此可见,凡是state的数据,都不应该放在data中,它会实时改变,所以都应该放在computed中

现在还剩一个小功能,就是在点击链接recipe1,recipe2时,能够切换食谱,


在JS中,其通过以下实现

['load','hashchange'].forEach(ev => window.addEventListener(ev,this.showRecipe))

在这里插入图片描述


但是在Vue中,通常会在组件的生命周期钩子中添加事件监听器,而不是直接在全局的window对象上。
在这里插入图片描述
在这里插入图片描述


移除事件监听器看起来是多余的,但我认为这是一个好习惯。

  • 内存泄漏:如果事件监听器引用了Vue组件的实例,那么这个实例将无法被垃圾回收机制回收,导致内存泄漏。这通常发生在组件被销毁后,但事件监听器仍然存在的情况下。

  • 性能问题:如果事件监听器引用了组件的方法,即使组件已经不再使用,这些方法仍然会被保留在内存中。随着应用的运行,可能会添加更多的事件监听器,从而占用更多的内存,导致性能下降。

  • 意外行为:如果事件监听器引用了组件的状态,而这些状态在组件销毁后不再更新,那么事件监听器可能会触发一些不再相关的操作,导致意外的行为。

  • 维护困难:不移除事件监听器可能会使得代码的维护变得更加困难,因为其他开发者可能不知道这些全局事件监听器的存在,从而在添加或移除事件监听器时产生冲突。


9. 回顾JS和Vue关于状态管理的框架对比

在这里插入图片描述
在这里插入图片描述
可以看到JS其实也是将数据单独拿出来,然后通过一个函数去改变这个状态,再由外部去调用这个model中的状态和行为。
在这里插入图片描述
在JS中,弊端就是一个model里的data要通过一个controller中间件传来传去,而Vue的状态是一个全局状态,你只需要关注你需要什么数据,直接通过getters就能得到,不需要通过函数之间传来传去。
在这里插入图片描述
同时,
在这里插入图片描述

10. ⚡需求五:配置文件config.js和helper.js

在JS中,比如请求的url是固定的,并可能在多个位置出现,一般会放在config,js文件中,使得集中一个位置来修改,也便于理解。

此外,还会将反复需要的操作存放在helper.js中,以便于复用代码。

那么,不论是哪一种,都应该是一个全局的状态,就是当别人拿到你的代码,看到config就能明白代码文件中有哪些固定的变量,这样再去读你的项目的时候,就可以清楚你某某初始值是什么意思,在config修改。同时,其他代码文件也应该通过import来获取到这些固定值

在vue中,可以像JS一样设置配置文件,只是要多许多import,也可以使用状态管理,通过this.$store.getters来得到值。


  • 记得切换分支
    在这里插入图片描述

11. ⚡需求六: url中没有相应id时,显示错误

还是一样,采用组件化处理错误。
在这里插入图片描述
在这里插入图片描述


在recipe中注册好error组件
在这里插入图片描述
在这里插入图片描述


在这里插入图片描述
通过state传出去
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
外部通过getters获取
在这里插入图片描述
在这里插入图片描述
现在的情况是:有错误时(下图),无错误时(下下图)
在这里插入图片描述
在这里插入图片描述


现在就是要处理一些布局的问题,应该在有错误时显示错误,其他不显示;没有错误时不显示错误信息,其他正常显示:

在这里插入图片描述
然后添加 v-ifv-else
在这里插入图片描述
ok!
在这里插入图片描述
在这里插入图片描述


下一篇:【vue:把一个JS项目改成vue框架】forkify项目(二)

  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值