💡开始之前:要求至少已经学过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:
试一下是否移植成功:在App.vue中,
分别对应html中的
ps:因为是这个项目,它没有页面的切换,所以可能用不到routing。
一般有页面切换的项目,会重新建一个page文件夹。
那么我在这里建一个layout文件夹,可能更符合这个项目。
接下来,根据布局分裂出3个的component:
- 自此开始,在vue框架下根据需求逐一写(复制)出component的功能
这个需求并不属于任何 layout,是后台的功能,所以写在App里,并且数据保存在App里,也更利于向全局provide或props数据。
项目中提供的api:https://forkify-api.herokuapp.com/v2(要科学调用)
得到数据后,要渲染数据,就是说把数据传递到recipe组件中,在recipe这个模块下动态展示数据
接下来在中渲染,替换变量,
ps:以往JS这里是采用的模板字符串(Template String),如:
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布局完成:
在JS中:
现在已经完成了渲染某一个数据,接下来就是实现调取一系列数据,渲染一系列选项,然后通过左边的list选择食谱,在recipe组件里渲染。
选择具体某食谱,url就会改变,这里可以用到router。
但是,也由于这个项目的特殊性,它不是通过url跳转到某一个页面,而是所有操作都只在这一个页面中进行,通过url的改变执行某个函数;
它有的是不同的布局,不同的渲染部分,所以router这里只是通过url获取到id,再监听id,和JS做的事一样,并没有通过router来重定向到别的组件。
在JS中:
在Vue中也可以使用传统的window.location实现如下:
轻微使用了router:
这里有一个待解决的bug:就是访问时是,也就是说没有检测到 属性。
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文件夹里有几个vue文件),使用管理
- 其次,page会调用各种组件(组件统一存放在文件夹,里面有vue文件)
- 此外,components里面除了page使用到的组件,还会包括一些与有关的(BaseCard.vue、BaseButton.vue)和与有关的(TheHeader.vue)组件
- 最后,为了方便各组件调用的数据是同步更新后的,状态管理由vuex完成:建立文件夹和index.js文件管理modules,安装并引入,在store中又会有不同的modules,modules中包含index.js、getters.js、mutations.js、actions.js
由于接下来对原来的代码将改动很大,并且也并不清楚是否能完成需求四,所以可以在此时保存一个代码版本,我将使用 git
会变成
假如修改了代码,想要返回上一个版本,只需要
- 方式一:
可以查看所有发布的版本,
复制版本的号,
按退出。
上面的
- 方式二:
创建分支,
切换到分支,
添加修改过的代码,
提交,
查看分支,
按退出。
切换回主枝
不论是从 api 调过来的数据还是用户返回的数据,都应该统一管理,这样就不需要在每个调用数据的组件中更新数据的状态,所有人(组件)收到的数据都是动态变化的。
那么,对于这个项目,我们应该把获取到的recipe存在状态中进行管理。首先确保,
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外面。
接着上方,创建好框架之后几乎就是复制粘贴了,(记得在分支中进行操作,我的分支名称为)
首先这个index.js里面就应该存放我们的recipe数据:
既然引入了mutation、actions、getters,那么他们都必须要导出对象,即 ,可以先写上,避免报错。
现在我们一一试着设置这三个文件。明白内在逻辑就不难,首先数据的请求要放在action中,在mutation中改变(赋值)recipe,然后外部通过getters获取recipe。
首先把App.vue中对数据的操作移植到action中。
复制:
粘贴到action
既然action的结果要提交给mutation,那么mutation中就要把获得的的值更新到state中去,
接下来就是调用的问题,怎么样才能让外部调用到action中的 函数,从而引发recipe的改变?
又要怎么样才能让外部获取到新的recipe?
首先是在这两个位置去思考,
vuex设置的是通过一个dispatch来调用action中的操作,通过getters来获取state的实时值(当然也可以直接this.$store.state.recipe ,但不建议这么做,getters会显得很没有面子🙃)
data中有两个数据,他们变成state,所以getters肯定也要设置这两个值的获取方式,所以先写好
注意了,由于已经设置了namespace,所以必须通过模块名字才能获取到state,如果是 将获取不到state,同理对dispatch 。
好了,接下来,基本能把状态管理完成,但是在页面更新时,出现一个问题,如下:(
ps:在actions中,在getters中)
出现一个一直在加载的情况,说明并没有获取到最新的值,而一直保持,并且,从打印顺序看出,action是最后完成的,还没等action中请求完数据,这个里面的就都执行完了,所以这个action中的函数,看似在前面,实际是异步的。下面给出他们的完成顺序:4123
当我重新加载一下(比如随便去掉分号)这个App.vue组件(因为我想让重新执行一遍),此时就能够得到recipe,并能正常渲染,这是为什么呢?
因为此时,上一次的action中已经请求到了数据,并且state已经更改,这样data中通过getter获取state时,就已经得到了相应的数据,就能够渲染。
- 综上,问题就出在App.vue中,数据没有根据改变而自动更新,且没有仅更新数据,而不是通过重新加载页面来更新。
- 解决:
由此可见,凡是state的数据,都不应该放在data中,它会实时改变,所以都应该放在computed中
现在还剩一个小功能,就是在点击链接recipe1,recipe2时,能够切换食谱,
在JS中,其通过以下实现
但是在Vue中,通常会在组件的生命周期钩子中添加事件监听器,而不是直接在全局的window对象上。
移除事件监听器看起来是多余的,但我认为这是一个好习惯。
- 内存泄漏:如果事件监听器引用了Vue组件的实例,那么这个实例将无法被垃圾回收机制回收,导致内存泄漏。这通常发生在组件被销毁后,但事件监听器仍然存在的情况下。
- 性能问题:如果事件监听器引用了组件的方法,即使组件已经不再使用,这些方法仍然会被保留在内存中。随着应用的运行,可能会添加更多的事件监听器,从而占用更多的内存,导致性能下降。
- 意外行为:如果事件监听器引用了组件的状态,而这些状态在组件销毁后不再更新,那么事件监听器可能会触发一些不再相关的操作,导致意外的行为。
- 维护困难:不移除事件监听器可能会使得代码的维护变得更加困难,因为其他开发者可能不知道这些全局事件监听器的存在,从而在添加或移除事件监听器时产生冲突。
可以看到JS其实也是将数据单独拿出来,然后通过一个函数去改变这个状态,再由外部去调用这个model中的状态和行为。
在JS中,弊端就是一个model里的data要通过一个controller中间件传来传去,而Vue的状态是一个全局状态,你只需要关注你需要什么数据,直接通过getters就能得到,不需要通过函数之间传来传去。
同时,
在JS中,比如请求的url是固定的,并可能在多个位置出现,一般会放在config,js文件中,使得集中一个位置来修改,也便于理解。
此外,还会将反复需要的操作存放在helper.js中,以便于复用代码。
那么,不论是哪一种,都应该是一个全局的状态,就是当别人拿到你的代码,看到config就能明白代码文件中有哪些固定的变量,这样再去读你的项目的时候,就可以清楚你某某初始值是什么意思,在config修改。同时,其他代码文件也应该通过import来获取到这些固定值
在vue中,可以像JS一样设置配置文件,只是要多许多import,也可以使用状态管理,通过this.$store.getters来得到值。
- 记得切换分支
还是一样,采用组件化处理错误。
在recipe中注册好error组件
通过state传出去
外部通过getters获取
现在的情况是:有错误时(下图),无错误时(下下图)
现在就是要处理一些布局的问题,应该在有错误时显示错误,其他不显示;没有错误时不显示错误信息,其他正常显示:
然后添加 和 :
ok!
下一篇:【vue:把一个JS项目改成vue框架】forkify项目(二)
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/15051.html