将现有传统的php网站项目迁移到vue-cli上

这是一个复杂的工程,但是每一步都是可寻的。所有迁移过程步骤都是可运行的,

本人实践的是一个电子表格项目

为大家提供一个平滑迁移的策略框架

https://github.com/13601313270/tableHub

真个迁移过程的第一次提交开始于Nov 7, 2017

https://github.com/13601313270/tableHub/commit/7b558d89931d627384d6e3c98fd435316ff61b74

先说一下我的现状

这是一个传统的php网站,用php的smarty模板将变量打到模板文件,生成对应的html。js采用的是传统的es5的js代码,在html的head头部引入

image

无论用什么主流还是不主流的框架,传统的php网站都是这个套路,新建一个页面对象,然后通过php添加很多个变量给smarty,然后让smarty去fetch模板文件,将变量和模板结合生成html

再来看一下模板文件

image

对smarty的前置过滤器做了优化,最终的html中站内js会有合并成一条js引用,顺序依照模板js顺序,不会破坏依赖关系。然后js进行压缩。但所有的js是用es5写的,smarty的include实现的组件,真的只是html拼接上,无法实现vue组件的变量传递。经常会组件生命全局变量污染变量。

有很大的工作量需要做,前后端分离是个大过程。

第一步,将所有的smarty变量输出点,改用ajax请求来输出。

然后在主html尾部增加一个请求这个地址的代码。

这个请求需要带上网址中的所有参数,比如下面,我们就带上了fileId参数

image

可以新建一个独立php文件,用于接收ajax请求,返回数据,改造成一个同样返回值的json对象。

image
image

生成一个有style,data,isMyTable属性的对象并生成json。

接下来就是细心的时候了,去找所有用到变量的地方,改为用js去实现

所有引用tpl。(如果不保证tpl只被这一个变量调用。可以新建一个同名代后缀的组件tpl。比如tools.tpl可以新建一个tools.state.tpl)。

我们以isMyTable变量举例,isMyTable为true的时候有一个editChange样式的div节点

image

我们改造为,先将这个元素隐藏,js判断如果为真,改为显示,否则移除dom

image
image

接下来我们就得到了一个可运行,跟原有功能一致的改动,这是一个平滑的改动,即使发布到线上也不会有bug(只不过会多增加两倍的服务接口请求)。可以commit一下代码到本地分支了。

接下来循环执行这个辛苦的事情,我们检查主模板和组件的模板,将所有使用变量的地方,改为js来实现功能

比如smarty的foreach循环改为js的for循环添加dom

在自己完全确认已经没有模板文件使用php传递进来的变量后,最好再用编辑器的文件查询功能,查询一下isMyTable,tableStyle,tableData,title这几个字符串,验证一下是否还有遗漏的。

然后我们就可以删掉传递的数据逻辑,控制器精简为下面的样子。

image

这时候模板文件所有的动态数据,都是通过ajax请求拼接好的。

在经过自测,可执行后。最好再增加一些双保险

例如由测试工程师系统化测试,小比例用户灰度测试等。之后就可以安全的大范围上线了。

其实现在依然还是用的smarty,还会存在比如include等smarty方法。这么做的意义,是首先摆脱php请求数据来赋值hml的依赖,为以后前后端分离做好第一步。

image

组件迁移

将组件需要使用的函数定义放在中,将执行的语句,都放到mounted之中,因为只有在mounted的时候,组件的dom才存在于document里。先忽略语句是不是使用es6语法,直接加进去。最起码是可以保证代码是可以运行的。

迁移前

image

迁移后

image

迁移通用js方法,循环找每一个在index.html里使用的全局js的方法,

每个方法,单独做一个es6的js。然后将所有使用原有js的组件,都迁移到新js,因为所有组件都已经是使用工程化编写了。等所有调用这个js都已经改为工程化引用新js后,删除index.html的老引用。

慢慢的,index.html的js会越来越少,慢慢就没了

image

全局函数提取到单独的js文件中,用es6的写法

image

所有使用这个函数的模块,都通过引用来调用

image

最后删除原始的定义

到这为止得到了一个混合项目,既可以将后面的项目进行新式的vue开发,老的逻辑维持现状,将来会遇到很多针对某个模块的需求,或者某个模块的bug,这时候,很有可能被开发人员顺便的迁移到新方式。

但是因为代码相互依赖,一个jquery的点击事件,内部封装了若干jquery的操作,以至于代码不会纯粹到可以迁移而不去影响别的逻辑。所以即使过了很长事件,有可能依然被迁移的不多。

这时候,我建议先针对dom输出的逻辑进行迁移。因为这种代码执行起来最容易,不会影响太多的其他代码。

image

针对这种情况代码,可以改为用vue的v-for优先迁移。

攻坚战

排在第二种优先级的是这种将状态保存在dom属性上的逻辑,因为这种逻辑本身就是过去代码策略的失败。当很多属相都迁移到data上的时候,剩下的代码才更好迁移。但是这一块也是最容易遗漏的。后面我在使用中,尝试总结平滑迁移技巧。

image
image

这时候将dom属性改为使用vue的data迁移,变成了一个很危险的大工程,因为很容易拉下部分写入或者调用的场景,因为调用这个属性的地方,可以遍及很多个文件,所以不能想当然的以为当前文件都迁移了,就是安全了。这时候我们可以先拿写操作入手。

第一步修改本文件内所有的将所有修改这个dom属性的地方同时写入一个data。commit一下,因为这个环节是“安全的”,就是说拉下一个两个的,也不会有任何bug,因为没有任何地方使用这个data

image

下面整个项目搜索一下,尽可能找到所有使用这个dom属性的地方。如果没有,则不用进行下面这一段的步骤。直接进入下一段监听环节

如果有则

因为vue的data作用于在本文件中,所以别的模板文件修改dom属性,我们没办法做到同时修改这个data,我们增加一个队data的监听,当data变化的时候,同时修改dom的属性

image

在所有使用这个组件的page中,增加一个vue的data,例如取名为child_props1,然后作为参数,组件调用的时候传递给组件。可以commit一下,因为这一步也是安全的。

如果加载插件的页面有修改dom属性,则额外增加一个修改child_props1的逻辑。

如果页面加载的其他组件修改了dom,则在所有修改属性的地方,额外增加一个事件来出发这个回调。

image

然后在page中监听这个事件,然后修改data。

可以将时间以temp_开头,用以说明是迁移过程中临时产生的。当然如果你认为这个值确实应该是页面全局的,也可以不加temp_

可以commit一下,因为到这也是安全无bug的

监听环节

当然,有可能拉下了一些修改dom属性的地方。

这时候,我们在组件某个使用属性地方加一个检测

image

我们只用在部分,或者1个最常用的地方,加上检测。保证能覆盖所有用户访问情况即可。当与预期不一致的时候进行一定的日志,或者输出到控制台。

这个检测的过程可以是:

测试工程师规范化的普测,然后监听控制台。

也可以是用户大量测试,然后发送日志给服务器。休息一会干点别的事情,一个小时后再来看看日志

可以发送当前url,或者其他信息。

等保证了所有修改dom的属性的地方都有覆盖到的时候,则去掉所有的修改dom属性的代码。

这个过程,可以去掉一个,测试一个。

去掉一个,测试一个。

等所有都去掉后,commit一下。

再次进行一次普测,如果没有再收到不一致报警。则可以安全的去将组件内所有调用dom属性的地方,改为取data值。然后commit

然后去掉组件内的watch修改dom属性的逻辑

image

这时一个安全的迁移已经完成了。

将写在body中和写在js中的函数迁移到vue的method中,一个很大的坑,就是this变量。vue的method中的this代表的是vue对象。所以,不要讲包含this的函数随意迁移进method。

除非发现这个函数所有的调用,都已经在vue结构内部,否则你把这个函数迁移进去,所有调用方都将找不到。

但是反过来说,所有对旧函数的调用代码都迁移到vue内部,但是函数在外部并不会造成任何的bug。所以函数的迁移永远是靠后的。

优先迁移这种执行形代码,这种代码处于依赖链头部。就像下面这一段,它依赖selectTd函数,但是没有任何代码依赖它。

这种典型的jquery项目的事件绑定,进行若干步骤,迁移进vue里

image

首先将this提取,vue项目的this是一个和jquery的this格格不入的东西。改成一个中间变量来代替this。

image

可以commit

这时候我们找到绑定这个事件的受体#tablePanel,在上面添加一个vue的对应事件,jquery的事件是click,所以增加一个vue的@click

image

需要传入$event参数,下面我们在methods中创建一个函数

image

我们在methods中创建对应一个函数

jquery的on事件委托的dom节点是满足选择器.edit #myTabContent td的

我们通过下面的逻辑,实现跟jquery一样的逻辑,找到委托节点

[
复制代码

](javascript:void(0); "复制代码")

if ((event.target).parents('.edit #myTabContent td'); if (eventDom.length === 0) { return;
} else {
eventDom = eventDom[0]
}
}

[
复制代码

](javascript:void(0); "复制代码")

接下来,我们就可以把执行内容部分,copy到刚刚新建的函数里了。

image
image

然后删除之前的这一段jquery方式的事件绑定,防止执行两次。就可以执行测试去了。

测试通过,进行commit。

虽然这个代码最终内部依然使用了jquery,但是他的意义在于,为类似selectTd这样的函数迁移进vue内部扫清障碍。是个中间步骤。

等待越来越多的这样执行被迁移,你会发现有很多的全局变量和全局函数,都是只被vue内部调用。这时候就是他们可以迁移的时候了。

这种方式可以迁移click,但是迁移用on绑定的mouseenter的时候,需要注意改为mouseover事件,否则,每一次的进入都是以整体才能捕获到。

image

配合使用刚刚的click的方法,可以捕获到真正想绑定的选择器dom,但是会有重复出发的情况出现,我们在某一个【.edit #myTabContent td】里移动会一直出发mouseover,这时候我们需要加一个记录,只有变化了另一个td,才去出发后面的执行,否则return掉。

[
复制代码

](javascript:void(0); "复制代码")

methods: {
lastEnterTd: '',//用于记录最后一次出发的dom
mouseenter_temp(event) { if ((event.target).parents('.edit #myTabContent td'); if (eventDom.length === 0) { return } else {
eventDom = eventDom[0]
}
} // 防止重复出发
if(this.lastEnterTd === eventDom){ return;
}
this.lastEnterTd = eventDom; //要执行的逻辑
console.log(eventDom);
}
}

[
复制代码

](javascript:void(0); "复制代码")

大的方针,就是会有很多个中间状态,中间状态也许会有很多旧的方式。但是只要这个中间状态能为后面的迁移打通障碍,就值得去做。就像华容道一样,理解了这个初衷,那么这个步骤就是成功的,所以就不在继续优化这个函数内部,就到这就暂告一段,日后自然还有机会去优化。

下面进入了漫长而无法推动

中间态的代码有若干特点。项目被vue化了,但是项目内部有很多函数会执行jquery的方法。比如下面这个

image

这时我们可以将vue和jquery的夹缝处改为使用原始的js的dom来作为交互接缝。因为vue和jquery都支持dom的读写操作

image

vue组件是支持嵌套组件的,所以使用dom来间接操作vue的虚拟元素,肯定不是最终态。但是vue组件嵌套需要修改被嵌套和嵌套都改成vue组件才能执行。被嵌套的子组件有可能内部还有子组件。所以就会演变成需要“同时”把所有地方都修改为vue组件,这是不可能安全的被推动的。

所以通过dom来间接修改,可以保证可以一个一个一个一个的去逐步攻克所有的组件。

通用js全局变量然后

摆脱jquery,

摆脱jquery的路上有一堆障碍物,这些障碍物叫做jquery插件。我们可以寻找替换的vue插件,或者用vue的方式替换这些插件。

比如我的项目中使用了dragging插件,就是可以拖动dom的,我可以自己去写鼠标事件来去掉使用dragging插件

image

比如我实现了一个vue的拖动插件

image

你可能感兴趣的:(将现有传统的php网站项目迁移到vue-cli上)