Vue1.x 迁移 Vue2.x 实战

JonWendt 8年前
   <p>本篇文章记录了完整的前端 Vue1.x 项目迁移至 Vue2.x 的步骤和遇到的问题。并且在迁移的过程也对Vue进行进一步学习。</p>    <h2>1 为什么要迁移 Vue2.x</h2>    <p>由于项目后面希望用到饿了么的 vue 组件,而且当前很多组件都是基于 Vue2.x 的,对于 Vue1.x 都已经不再维护。</p>    <p>Vue2.x 更加成熟,并且后面准备在移动端接入 weex ,而 weex 中也推荐使用 Vue2.x 进行开发。</p>    <p>综上所述,下定决心开始对 Vue1.x 的项目进行迁移。</p>    <h2>2 前期准备</h2>    <p>先介绍下原有项目的大致情况。</p>    <ol>     <li>项目不是单纯的单页面应用,每个大模块是一个单页面应用,大模块中的子模块的跳转使用路由进行跳转。</li>     <li>原有项目 vue 版本号1.0.21,vue-router 版本号0.7.13,未使用 vuex ,网络请求用的就是 jquery</li>    </ol>    <p>希望在迁移后,新功能的添加可以完全使用单页面应用,并且配合用上 vuex 和网络请求库( axios )还有一些其他的插件。</p>    <p>现附上在迁移过程中相关文档地址:</p>    <ol>     <li><a href="/misc/goto?guid=4959742710672811181" rel="nofollow,noindex">Vue 官方迁移文档</a><br> 这也是迁移过程中主要的参考文档</li>     <li><a href="/misc/goto?guid=4959742710762672270" rel="nofollow,noindex">vue-migration-helper</a><br> 官方迁移的辅助工具,用以遍历查找出需要迁移的地方。识别出旧有的特性后,就会告知并给出建议,同时附上关于详细信息的链接。</li>     <li><a href="/misc/goto?guid=4959742710851083350" rel="nofollow,noindex">vue-router 2.x 官方文档</a></li>     <li>万能的 Google</li>    </ol>    <h2>3 开始迁移</h2>    <h3>3.1 检查出迁移的地方</h3>    <p>先安装官方迁移工具 <a href="/misc/goto?guid=4959742710762672270" rel="nofollow,noindex">vue-migration-helper</a></p>    <pre>  <code class="language-javascript">npm install --global vue-migration-helper</code></pre>    <p>安装完成后进入项目目录,扫描项目中文件找出需要迁移的代码位置。(并不能完全找出,但是可以解决大部分的迁移)</p>    <p>我的迁移做法是扫描全局后,专注修改一类迁移问题,修改完后commit并且再次扫描确认该类问题已经解决。然后再解决下一类的问题。</p>    <p>第一次扫描并输出到文件中</p>    <pre>  <code class="language-javascript">vue-migration-helper >a.log</code></pre>    <p>输出一看,WTF!!!500多个迁移点,网上人家迁移就几十个迁移点。呵呵哒了。</p>    <p><img src="https://simg.open-open.com/show/34393a586855c33f5450334f92d6cd41.png"></p>    <p>第一次扫描结果</p>    <h3>3.2 package.json修改</h3>    <p>首先提示的是修改 package.json 中的版本号,并重新 npm install</p>    <p><img src="https://simg.open-open.com/show/b654b010ab1b0031b07a9cda4f9304c0.png"></p>    <p>package.json 修改</p>    <p>这个没啥,我直接升到了最新的vue和vue-router版本</p>    <h3>3.3 v-link 替换</h3>    <p>第一个解决的是 v-link 提示信息如下</p>    <p><img src="https://simg.open-open.com/show/bf2b120e7cf71367487e94d9f235c4da.png"></p>    <p>替换 v-link</p>    <p>找到相关文档 <a href="/misc/goto?guid=4959742710954515033" rel="nofollow,noindex">https://cn.vuejs.org/v2/guide/migration-vue-router.html#v-link-替换</a></p>    <p>文档描述是把</p>    <pre>  <code class="language-javascript"><a v-link="'/about'">About</a></code></pre>    <p>替换成</p>    <pre>  <code class="language-javascript"><router-link to="/about">About</router-link></code></pre>    <p>But,项目中是这么写的</p>    <pre>  <code class="language-javascript"><button class="btn btn-default pull-right" v-link="{ path: '/'}">返回</button></code></pre>    <p>查询最新的 vue-router 文档,可以使用编程式导航,改成如下</p>    <pre>  <code class="language-javascript"><button class="btn btn-default pull-right" @click="$router.push({ path: '/'})">返回</button></code></pre>    <p>然后查找所有 v-link 类型问题一个个全部改过来,commit 提交。重新扫描一次。</p>    <h3>3.4 $index 和 $key 的移除</h3>    <p>$index 和 $key 的移除问题的提示是这样的</p>    <p><img src="https://simg.open-open.com/show/ddc3e43f1c1826c8d11c72453df97dee.png"></p>    <p>$index 和 $key 的移除</p>    <p>文档见 <a href="/misc/goto?guid=4959742711039745435" rel="nofollow,noindex">https://cn.vuejs.org/v2/guide/migration.html#index-and-key-移除</a></p>    <p>主要是 Vue2.x 中移除了隐变量 $index 和 $key ,全部显示声明。修改例子如下</p>    <pre>  <code class="language-javascript"><tr v-for="clinic of clinicDatas">      <td>{{ $index+1 }}</td>  </tr></code></pre>    <p>改为:</p>    <pre>  <code class="language-javascript"><tr v-for="(clinic, index) of clinicDatas">      <td>{{ index+1 }}</td>  </tr></code></pre>    <p>回头重新看了下 v-for 的文档</p>    <p>默认命名 index 代表索引, key 代表遍历对象的键值。</p>    <p>比如遍历一个数组的时候,第二个参数是 index 索引</p>    <pre>  <code class="language-javascript"><ul id="example-2">    <li v-for="(item, index) in items">      {{ parentMessage }} - {{ index }} - {{ item.message }}    </li>  </ul></code></pre>    <p>当遍历对象时,第二个参数是 key 键</p>    <pre>  <code class="language-javascript"><div v-for="(value, key) in object">    {{ key }} : {{ value }}  </div></code></pre>    <p>同时如果遍历对象,还可以有第三个参数代表索引</p>    <pre>  <code class="language-javascript"><div v-for="(value, key, index) in object">    {{ index }}. {{ key }} : {{ value }}  </div></code></pre>    <h3>3.5 HTML 的计算插值移除 ({{{ }}})</h3>    <p>HTML 的计算插值移除 提示是这样的</p>    <p><img src="https://simg.open-open.com/show/52a7768c109d45150fc5c6474524ddd3.png"></p>    <p>HTML 的计算插值移除</p>    <p>文档见 <a href="/misc/goto?guid=4959742711125845848" rel="nofollow,noindex">https://cn.vuejs.org/v2/guide/migration.html#HTML-计算插值-移除</a></p>    <p>这个替换比较简单,就是把 {{{}}} 全部替换为 v-html</p>    <h3>3.6生命周期 ready 替换</h3>    <p>生命周期 ready 替换 提示是这样的</p>    <p><img src="https://simg.open-open.com/show/7b6b0ef7c30459f38f9872261a75d245.png"></p>    <p>生命周期 ready 替换</p>    <p>要把生命周期钩子函数 ready 替换为以下:</p>    <pre>  <code class="language-javascript">mounted: function () {    this.$nextTick(function () {      // 代码保证 this.$el 在 document 中    })  }</code></pre>    <p>在这里我回头看了下 nextTick 的具体概念: <a href="/misc/goto?guid=4959742711216623814" rel="nofollow,noindex">https://cn.vuejs.org/v2/guide/reactivity.html#异步更新队列</a></p>    <p>意思就是当数据发生变化后 DOM 不会立即更新,而是会在一个更新周期时统一更新(感觉就像 Android 的16ms 渲染刷新)。所以使用 nextTick ,是一个异步执行,意思是方法里面的代码会在下次 DOM 更新后执行。</p>    <h3>3.7 Array-prototype-$remove 移除</h3>    <p>Array-prototype-$remove 移除问题的提示:</p>    <p><img src="https://simg.open-open.com/show/492608aa315a53f865b20de29f8e53da.png"></p>    <p>Array-prototype-$remove 移除</p>    <p>文档见 <a href="/misc/goto?guid=4959742711298555175" rel="nofollow,noindex">https://cn.vuejs.org/v2/guide/migration.html#Array-prototype-remove-移除</a></p>    <p>数组的一个 remove 方法移除了,换成了 splice 方法。</p>    <pre>  <code class="language-javascript">vm.articleList.$remove(vm.temArticle)</code></pre>    <p>改成</p>    <pre>  <code class="language-javascript">var index = vm.articleList.indexOf(vm.temArticle);  vm.articleList.splice(index, 1)</code></pre>    <h3>3.8 v-el 和 v-ref 替换</h3>    <p>v-el 和 v-ref 替换问题的提示:</p>    <p><img src="https://simg.open-open.com/show/29a140b2364217fe77c84af78aa9df20.png"></p>    <p>v-el 和 v-ref 替换</p>    <p>文档见: <a href="/misc/goto?guid=4959742711383653297" rel="nofollow,noindex">https://cn.vuejs.org/v2/guide/migration.html#v-el-和v-ref-替换</a></p>    <p>直接全局将 v-el 和 v-ref 换成 ref</p>    <h3>3.9 属性内部的计算插值移除 ({{ }})</h3>    <p>属性内部的计算插值移除的提示:</p>    <p><img src="https://simg.open-open.com/show/9d56a93935f9216fbf1471be6d30a112.png"></p>    <p>属性内部的计算插值移除</p>    <p>文档见: <a href="/misc/goto?guid=4959742711463098112" rel="nofollow,noindex">https://cn.vuejs.org/v2/guide/migration.html#属性内部的计算插值-移除</a></p>    <p>属性内部的计算插值已经不能再使用了:</p>    <pre>  <code class="language-javascript"><button class="btn btn-{{ size }}"></button></code></pre>    <p>应该写成行内表达式:</p>    <pre>  <code class="language-javascript"><button v-bind:class="'btn btn-' + size"></button></code></pre>    <h3>3.10 v-for 遍历数组/对象时的参数顺序变更</h3>    <p>v-for 遍历数组/对象时的参数顺序变更问题的提示:</p>    <p><img src="https://simg.open-open.com/show/263048633326d915b54e3e07d070b9c3.png"></p>    <p>v-for 遍历数组/对象时的参数顺序变更</p>    <p>文档见: <a href="/misc/goto?guid=4959742711549428783" rel="nofollow,noindex">https://cn.vuejs.org/v2/guide/migration.html#v-for-遍历数组时的参数顺序-变更</a></p>    <p>当包含 index key 时,之前遍历数组时的参数顺序是 (index, value) 。现在是 (value, index) , 目的为了和 Javascript 原生中的顺序保持一致。</p>    <h3>3.11 router-go 改变</h3>    <p>router-go 改变 问题的提示是:</p>    <p><img src="https://simg.open-open.com/show/20364f823e0be445370f054e7e74136e.png"></p>    <p>router-go 改变</p>    <p>文档见: <a href="/misc/goto?guid=4959742711632694900" rel="nofollow,noindex">https://cn.vuejs.org/v2/guide/migration-vue-router.html#router-go-改变</a></p>    <p>直接全局替换 router-go 为 router-push</p>    <h3>3.12 track-by 替换</h3>    <p>track-by 替换问题的提示是:</p>    <p><img src="https://simg.open-open.com/show/7281360df7a4e5c3ed5c89b502deaca2.png"></p>    <p>track-by 替换</p>    <p>文档见: <a href="/misc/goto?guid=4959742711724624222" rel="nofollow,noindex">https://cn.vuejs.org/v2/guide/migration.html#track-by-替换</a></p>    <p>直接代码全局替换 track-by 为 :key</p>    <h3>3.13 router 路由定义的替换</h3>    <p>router 替换的提示是:</p>    <p><img src="https://simg.open-open.com/show/c92dc55b0a841641cbe0bb6333f6b9be.png"></p>    <p>router 路由定义的替换</p>    <p>文档见: <a href="/misc/goto?guid=4959742711804414884" rel="nofollow,noindex">https://cn.vuejs.org/v2/guide/migration-vue-router.html#router-map-替换</a></p>    <p>这个地方比较麻烦,要把所有路由定义都修改了。包括子路由的定义。原来的代码是:</p>    <pre>  <code class="language-javascript">import Vue from 'vue'  import Router from 'vue-router'  ...    Vue.use(Router)  var router = new Router()  router.map({//定义路由映射    '/': {      name: 'main',      component: Main,      subRoutes: {        '/': {          name: 'list',          component: List        },        '/list': {          name: 'list',          component: List        }      },    },    '/add/:assetId': {      name: 'add',      component: Add    }  });  router.start(Assets, '#app')</code></pre>    <p>修改为:</p>    <pre>  <code class="language-javascript">import Vue from 'vue'  import VueRouter from 'vue-router'  ...    Vue.use(VueRouter)    var router = new VueRouter({    routes: [      {        path: '/',        name: 'main',        component: Main,        children: [          {            path: '/',            name: 'list',            component: List          },          {            path: '/list',            name: 'list',            component: List          }        ]      },      {        path: '/add/:assetId',        name: 'add',        component: Add      }    ]  })    const vm = new Vue({    router,    render: h => h(Assets)  }).$mount('#app')</code></pre>    <h3>3.14 v-bind 的 once 和 sync 修饰符移除</h3>    <p>v-bind 的 once 和 sync 修饰符移除问题提示是:</p>    <p><img src="https://simg.open-open.com/show/7dd6f995bc43452a5c739f3fd65bc5f0.png"></p>    <p>v-bind 的 once 和 sync 修饰符移除</p>    <p>文档见: <a href="/misc/goto?guid=4959742711882052552" rel="nofollow,noindex">https://cn.vuejs.org/v2/guide/migration.html#v-bind-的-once和-sync-修饰符-移除</a></p>    <p>这个问题将近 100 个地方,因为项目中定义了大量的组件。比如 翻页组件、日期组件、上传组件、地点组件等等。</p>    <p>在 Vue1.x 时可以增加 sync 修饰符实现父组件和子组件的双向绑定,但是到了 Vue2.x prop 只能单向传递,意思就是只能父组件以 prop 方式将数据传入子组件,但是子组件中不可以对 prop 中的值进行修改,即无法双向绑定。</p>    <p>如果想要通知父组件进行数据修改需要定义组件事件,然后子组件中使用 $emit(eventName) 触发事件,父组件中使用 $on(eventName) 监听事件。</p>    <p>这个问题改的比较多,消耗了很多的时间。首先我要把涉及的组件重新设计,改成反向事件触发修改父组件属性,然后父组件要监听修改函数。</p>    <h2>4 编译项目</h2>    <p>至此,基本上所有工具扫描出来的问题基本上改完了。但这还远远不够,当我 npm run dev 时又报出来很多错误。基本上总结以下几个问题。</p>    <h3>4.1 template 下只能有一个根组件</h3>    <p>问题报错:</p>    <p><img src="https://simg.open-open.com/show/e5ee26dc0f6ace9946c1209156b5047e.png"></p>    <p>template 下只能有一个根组件</p>    <p>原来是我的 vue 文件中的 template 节点下有多个 div 节点。不知道为什么 Vue1.x 没有报错。全部统一为一个 div 根节点。</p>    <h3>4.2 delete 方法被占用</h3>    <p><img src="https://simg.open-open.com/show/502e7dd9cae70de9b9fb3f2f090845c4.png"></p>    <p>Paste_Image.png</p>    <p>我的一些方法定义是 delete ,这时报错 delete 是 JavaScript 的关键字。我需要所有修改为另一个名称。</p>    <pre>  <code class="language-javascript"><modal title="系统提示" text="确定删除吗?" id="deleteModal" :confirm-action="delete"></modal></code></pre>    <h3>4.3 重复的 :class 和 :click</h3>    <p>这个可能是我在迁移的时候一个标签里有了重复的 :class , :click 属性。自己删除或者整合一下就行</p>    <h2>5 运行检查</h2>    <p>编译通过后,终于可以跑起来了。这时候就需要每个页面进行测试,每个操作进行测试。发现了以下几个问题</p>    <ol>     <li>router.afterEach 方法有了修改,工具没有识别出来,手动查询做了修改<br> 文档可以参见 <a href="/misc/goto?guid=4959742711976151175" rel="nofollow,noindex">https://github.com/vuejs/vue-router/blob/1.0/docs/zh-cn/api/after-each.md</a> 和 <a href="/misc/goto?guid=4959742712062896302" rel="nofollow,noindex">http://router.vuejs.org/zh-cn/advanced/navigation-guards.html</a> 识别前后版本的区别。</li>     <li>Vue.$set 方法已经废弃但是未被识别,手动查询做了修改</li>     <li>2.x 中增加了 HTML 保留关键字,之前我定义的 content address 组件都不能叫做这个名字了。要么首字母改成大写。要么换个名字。<br> 详细可见: <a href="/misc/goto?guid=4959742712145600322" rel="nofollow,noindex">https://jingsam.github.io/2016/10/30/vue-components-naming.html</a></li>    </ol>    <p>所有页面跑过一遍,很多小细节慢慢调试修改,一定要细心再细心。(改的最多的还是组件的部分)</p>    <h2>结尾</h2>    <p>最后500多个问题总共耗时2天多的时间才完全跑通原有的项目。不过升级到 Vue2.x 后首先做的就是引入了饿了么组件,感觉方便很多,还有一些其他插件在升级后都可以方便的使用。</p>    <p>注:以上只是我升级迁移过程遇到的问题。每个项目不同会有不同的问题,建议以官方迁移文档为主。</p>    <p> </p>    <p>来自:https://juejin.im/post/58d1046cb123db3f6b4df9f7</p>    <p> </p>