migration-to-vue3.md 7.5 KB
Newer Older
Q
qiang 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
#### vue2 项目迁移 vue3,必须适配的部分

以下列举迁移到 vue3,必须适配的几个点,vue2 项目才能正常运行在 vue3 上。更多查看完整的[非兼容特性列表](https://github.com/vuejs/vue-next/tree/master/packages/vue-compat#incompatible)

- main.js

  - 创建应用实例

    ```JS
    // 之前 - Vue 2
    import Vue from 'vue'
    import App from './App'
    Vue.config.productionTip = false    // vue3 不再需要
    App.mpType = 'app'    // vue3 不再需要
    const app = new Vue({
    ...App
    })
    app.$mount()

    // 之后 - Vue 3
    import App from './App'
    import { createSSRApp } from 'vue'
    export function createApp() {
       const app = createSSRApp(App)
       return {
          app
       }
    }
    ```

  - 全局属性,例如:全局网络请求

  ```js
  // 之前 - Vue 2
  Vue.prototype.$http = () => {};

  // 之后 - Vue 3
  const app = createApp({});
  app.config.globalProperties.$http = () => {};
  ```

  - 插件使用,例如:使用 vuex 的 store

  ```js
  // 之前 - Vue 2
  import store from "./store";
  Vue.prototype.$store = store;

  // 之后 - Vue 3
  import store from "./store";
  const app = createApp(App);
  app.use(store);
  ```

- 项目根目录必需创建 index.html 文件,粘贴复制如下内容:

  ```html
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8" />
F
fasttian 已提交
62 63 64 65
      <meta
        name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
      />
Q
qiang 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
      <title></title>
      <!--preload-links-->
      <!--app-context-->
    </head>
    <body>
      <div id="app"><!--app-html--></div>
      <script type="module" src="/main.js"></script>
    </body>
  </html>
  ```

- 只支持使用 ES6 模块规范,commonJS 需改为 ES6 模块规范

  - 导入模块, 例如:

    ```js
    // 之前 - Vue 2, 使用 commonJS
    var utils = require("../../../common/util.js");

    // 之后 - Vue 3, 只支持 ES6 模块
    import utils from "../../../common/util.js";
    ```

  - 模块导出,例如:

    ```js
    // 之前 - Vue 2, 依赖如使用 commonJS 方式导出
    module.exports.X = X;

    // 之后 - Vue 3, 可手动改为 ES6 导出
    export default { X };
    ```

- vuex 用法

  ```js
  // 之前 - Vue 2
    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
    const store = new Vuex.Store({
        state: {}
    })
    export default store

  // 之后 - Vue 3
    import { createStore } from 'vuex'
    const store = createStore({
        state: {}
    })
    export default store
  ```

- 避免在同一元素上同时使用 v-if 与 v-for

  > 而 Vue3 中,v-if 总是优先于 v-for 生效。以上写法将会在 Vue3 中与预期不符合,由于语法上存在歧义,建议避免在同一元素上同时使用两者([更多](https://v3.cn.vuejs.org/guide/migration/v-if-v-for.html#%E6%A6%82%E8%A7%88))。

- 生命周期的适配
F
fasttian 已提交
124 125 126

  在 Vue3 中组件卸载的生命周期被重新命名

Q
qiang 已提交
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
  - `destroyed` 修改为 `unmounted`
  - `beforeDestroy` 修改为 `beforeUnmount`

- 事件的适配

  Vue3 现在提供了一个`emits`选项,类似于现有`props`选项。此选项可用于定义组件可以向其父对象发出的事件, [更多](https://v3.cn.vuejs.org/guide/migration/emits-option.html#overview)

  **强烈建议使用`emits`记录每个组件发出的所有事件。**

  这一点特别重要,因为去除了`.native`修饰符。`emits` 现在在未使用声明的事件的所有侦听器都将包含在组件的中`$attrs`,默认情况下,该侦听器将绑定到组件的根节点。

  ```html
  <template>
    <button @click="onClick">OK</button>
  </template>
  <script>
F
fasttian 已提交
143 144 145 146 147 148 149 150
    export default {
      emits: ["click"],
      methods: {
        onClick() {
          this.$emit("click", "OK");
        },
      },
    };
Q
qiang 已提交
151 152 153 154
  </script>
  ```

- v-model 的适配
F
fasttian 已提交
155

Q
qiang 已提交
156 157 158 159 160 161 162 163 164 165
  Vue3 的 v-model 相对 Vue2 来说 ,有了较大的改变。可以使用多 `model`,相应语法也有变化。[更多](https://v3.cn.vuejs.org/guide/migration/v-model.html#%E6%A6%82%E8%A7%88)

  - 修改 modelValue
    用于自定义组件时,Vue3 v-model prop 和事件默认名称已更改 `props.value` 修改为 `props.modelValue` ,`event.value` 修改为 `update:modelValue`

    ```javascript
    export default {
      props: {
        // value:String,
        // 替换 value 为 modelValue
F
fasttian 已提交
166 167 168
        modelValue: String,
      },
    };
Q
qiang 已提交
169 170 171
    ```

- 事件返回
F
fasttian 已提交
172

F
fasttian 已提交
173
  将之前的 `this.$emit('input')` 修改为 `this.$emit('update:modelValue')` ,vue3 中将省略这一步骤
Q
qiang 已提交
174 175 176 177

  自定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue 事件:

  ```html
F
fasttian 已提交
178 179 180
  <ChildComponent v-model="pageTitle" />

  <!-- 是以下的简写: -->
Q
qiang 已提交
181

F
fasttian 已提交
182 183 184 185 186
  <ChildComponent
    :modelValue="pageTitle"
    @update:modelValue="pageTitle = $event"
  />
  ```
Q
qiang 已提交
187 188 189 190

  若需要更改 model 名称,作为组件内 model 选项的替代,现在我们可以将一个 argument 传递给 v-model:

  ```html
F
fasttian 已提交
191 192 193 194 195
  <ChildComponent v-model:title="pageTitle" />

  <!-- 是以下的简写: -->

  <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
Q
qiang 已提交
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
  ```

- 插槽的适配

  Vue3 将不支持 `slot="xxx"` 的用法 ,请使用 `v-slot:xxx` 用法。[更多](https://v3.cn.vuejs.org/guide/component-slots.html#%E5%85%B7%E5%90%8D%E6%8F%92%E6%A7%BD)

  ```html
  <!--  Vue2 支持的用法 -->
  <uni-nav-bar>
    <view slot="left" class="city">
      <!-- ... -->
    </view>
  </uni-nav-bar>
  ```

  ```html
  <!--  Vue3 支持的用法 -->
  <uni-nav-bar>
    <template v-slot:left>
      <view class="city">
        <!-- ... -->
      </view>
    </template>
  </uni-nav-bar>
  ```

F
fasttian 已提交
222 223
- 从 Vue 3.0 开始,过滤器已删除,不再支持,建议用方法调用或计算属性替换它们。[更多](https://v3.cn.vuejs.org/guide/migration/filters.html#%E6%A6%82%E8%A7%88)

D
DCloud_LXH 已提交
224
- 在 Vue3 中,处理 API `Promise 化` 调用结果的方式不同于 Vue2。[更多](https://uniapp.dcloud.io/api/#api-promise-化)
F
fasttian 已提交
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286

  - Vue3 中,调用成功会进入 then 方法,调用失败会进入 catch 方法
  - Vue2 中,调用无论成功还是失败,都会进入 then 方法,返回数据的第一个参数是错误对象,第二个参数是返回数据
  - Vue 2 写法转 Vue 3 写法

    ```js
    // 在 main.js 中写入以下代码即可
    function isPromise(obj) {
      return (
        !!obj &&
        (typeof obj === "object" || typeof obj === "function") &&
        typeof obj.then === "function"
      );
    }

    uni.addInterceptor({
      returnValue(res) {
        if (!isPromise(res)) {
          return res;
        }
        return new Promise((resolve, reject) => {
          res.then((res) => {
            if (res[0]) {
              reject(res[0]);
            } else {
              resolve(res[1]);
            }
          });
        });
      },
    });
    ```

  - Vue 3 写法转 Vue 2 写法

    ```js
    // 在 main.js 中写入以下代码即可
    function isPromise(obj) {
      return (
        !!obj &&
        (typeof obj === "object" || typeof obj === "function") &&
        typeof obj.then === "function"
      );
    }

    uni.addInterceptor({
      returnValue(res) {
        if (!isPromise(res)) {
          return res;
        }
        const returnValue = [undefined, undefined];
        return res
          .then((res) => {
            returnValue[1] = res;
          })
          .catch((err) => {
            returnValue[0] = err;
          })
          .then(() => returnValue);
      },
    });
    ```