Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
DCloud
unidocs-zh
提交
8dd0e592
unidocs-zh
项目概览
DCloud
/
unidocs-zh
通知
3216
Star
106
Fork
815
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
94
列表
看板
标记
里程碑
合并请求
70
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
unidocs-zh
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
94
Issue
94
列表
看板
标记
里程碑
合并请求
70
合并请求
70
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
8dd0e592
编写于
4月 08, 2022
作者:
inkwalk
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix: add
上级
281e1390
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
324 addition
and
287 deletion
+324
-287
docs/tutorial/migration-to-vue3.md
docs/tutorial/migration-to-vue3.md
+324
-287
未找到文件。
docs/tutorial/migration-to-vue3.md
浏览文件 @
8dd0e592
...
...
@@ -4,318 +4,321 @@
## main.js
-
创建应用实例
::: preview
> Vue2
```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()
```
> Vue3
创建应用实例
::: preview
> Vue2
```
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()
```
> Vue3
```
JS
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"
/>
<meta
name=
"viewport"
content=
"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<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
import App from './App'
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
```
:::
-
全局属性,例如:全局网络请求
```js
// 之前 - Vue 2
Vue.prototype.$http = () => {};
```
js
// 之前 - Vue 2, 使用 commonJS
var
utils
=
require
(
"
../../../common/util.js
"
);
// 之后 - Vue 3
const app = createApp({});
app.config.globalProperties.$http = () => {};
```
// 之后 - Vue 3, 只支持 ES6 模块
import
utils
from
"
../../../common/util.js
"
;
```
-
插件使用,例如:使用 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);
```
```
js
// 之前 - Vue 2, 依赖如使用 commonJS 方式导出
module
.
exports
.
X
=
X
;
-
项目根目录必需创建 index.html 文件,粘贴复制如下内容:
```
html
<!DOCTYPE html>
<html
lang=
"en"
>
<head>
<meta
charset=
"UTF-8"
/>
<meta
name=
"viewport"
content=
"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div
id=
"app"
>
<!--app-html-->
</div>
<script
type=
"module"
src=
"/main.js"
></script>
</body>
</html>
// 之后 - Vue 3, 只支持 ES6 模块
export
default
{
X
};
```
-
只支持使用 ES6 模块规范,commonJS 需改为 ES6 模块规范
## vuex 用法
-
导入模块, 例如:
::: preview
```js
// 之前 - Vue 2, 使用 commonJS
var utils = require("../../../common/util.js");
> Vue2
// 之后 - Vue 3, 只支持 ES6 模块
import utils from "../../../common/util.js";
```
```
js
import
Vue
from
"
vue
"
;
import
Vuex
from
"
vuex
"
;
Vue
.
use
(
Vuex
);
const
store
=
new
Vuex
.
Store
({
state
:
{},
});
export
default
store
;
```
-
模块导出,例如:
> Vue3
```js
// 之前 - Vue 2, 依赖如使用 commonJS 方式导出
module.exports.X = X;
```
js
import
{
createStore
}
from
"
vuex
"
;
const
store
=
createStore
({
state
:
{},
});
export
default
store
;
```
// 之后 - Vue 3, 只支持 ES6 模块
export default { X };
```
:::
-
vuex 用法
## 避免在同一元素上同时使用 v-if 与 v-for
::: preview
> 而 Vue3 中,v-if 总是优先于 v-for 生效。以上写法将会在 Vue3 中与预期不符合,由于语法上存在歧义,建议避免在同一元素上同时使用两者([更多](https://v3.cn.vuejs.org/guide/migration/v-if-v-for.html#%E6%A6%82%E8%A7%88))。
> Vue2
## 生命周期的适配
```
js
import
Vue
from
'
vue
'
import
Vuex
from
'
vuex
'
Vue
.
use
(
Vuex
)
const
store
=
new
Vuex
.
Store
({
state
:
{}
})
export
default
store
```
在 Vue3 中组件卸载的生命周期被重新命名
> Vue3
-
`destroyed`
修改为
`unmounted`
-
`beforeDestroy`
修改为
`beforeUnmount`
```
js
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))。
Vue3 现在提供了一个
`emits`
选项,类似于现有
`props`
选项。此选项可用于定义组件可以向其父对象发出的事件,
[
更多
](
https://v3.cn.vuejs.org/guide/migration/emits-option.html#overview
)
-
生命周期的适配
**强烈建议使用`emits`记录每个组件发出的所有事件。**
在 Vue3 中组件卸载的生命周期被重新命名
这一点特别重要,因为去除了
`.native`
修饰符。
`emits`
现在在未使用声明的事件的所有侦听器都将包含在组件的中
`$attrs`
,默认情况下,该侦听器将绑定到组件的根节点。
-
`destroyed`
修改为
`unmounted`
-
`beforeDestroy`
修改为
`beforeUnmount`
```
html
<template>
<button
@
click=
"onClick"
>
OK
</button>
</template>
<script>
export
default
{
emits
:
[
"
click
"
],
methods
:
{
onClick
()
{
this
.
$emit
(
"
click
"
,
"
OK
"
);
},
},
};
</script>
```
-
事件
的适配
## v-model
的适配
Vue3 现在提供了一个
`emits`
选项,类似于现有
`props`
选项。此选项可用于定义组件可以向其父对象发出的事件,
[
更多
](
https://v3.cn.vuejs.org/guide/migration/emits-option.html#overview
)
Vue3 的 v-model 相对 Vue2 来说 ,有了较大的改变。可以使用多
`model`
,相应语法也有变化。
[
更多
](
https://v3.cn.vuejs.org/guide/migration/v-model.html#%E6%A6%82%E8%A7%88
)
**强烈建议使用`emits`记录每个组件发出的所有事件。**
### 修改 modelValue
这一点特别重要,因为去除了
`.native`
修饰符。
`emits`
现在在未使用声明的事件的所有侦听器都将包含在组件的中
`$attrs`
,默认情况下,该侦听器将绑定到组件的根节点。
用于自定义组件时,Vue3 v-model prop 和事件默认名称已更改
`props.value`
修改为
`props.modelValue`
,
`event.value`
修改为
`update:modelValue`
```
html
<template>
<button
@
click=
"onClick"
>
OK
</button>
</template>
<script>
export
default
{
emits
:
[
"
click
"
],
methods
:
{
onClick
()
{
this
.
$emit
(
"
click
"
,
"
OK
"
);
},
},
};
</script>
```
javascript
export
default
{
props
:
{
// value:String,
// 替换 value 为 modelValue
modelValue
:
String
,
},
};
```
-
v-model 的适配
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
modelValue: String,
},
};
```
将之前的
`this.$emit('input')`
修改为
`this.$emit('update:modelValue')`
,vue3 中将省略这一步骤
-
事件返回
自定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue 事件:
将之前的
`this.$emit('input')`
修改为
`this.$emit('update:modelValue')`
,vue3 中将省略这一步骤
```
html
<ChildComponent
v-model=
"pageTitle"
/>
自定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue 事件:
<!-- 是以下的简写: -->
```
html
<ChildComponent
v-model=
"pageTitle"
/>
<ChildComponent
:modelValue=
"pageTitle"
@
update:modelValue=
"pageTitle = $event"
/>
```
<!-- 是以下的简写: -->
若需要更改 model 名称,作为组件内 model 选项的替代,现在我们可以将一个 argument 传递给 v-model:
<ChildComponent
:modelValue=
"pageTitle"
@
update:modelValue=
"pageTitle = $event"
/>
```
```
html
<ChildComponent
v-model:title=
"pageTitle"
/>
若需要更改 model 名称,作为组件内 model 选项的替代,现在我们可以将一个 argument 传递给 v-model:
<!-- 是以下的简写: -->
```
html
<ChildComponent
v-model:title=
"pageTitle"
/>
<ChildComponent
:title=
"pageTitle"
@
update:title=
"pageTitle = $event"
/>
```
<!-- 是以下的简写: -->
## 插槽的适配
<ChildComponent
:title=
"pageTitle"
@
update:title=
"pageTitle = $event"
/>
```
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
)
-
插槽的适配
::: preview
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
)
> Vue2
::: preview
```
html
<!-- Vue2 支持的用法 -->
<uni-nav-bar>
<view
slot=
"left"
class=
"city"
>
<!-- ... -->
</view>
</uni-nav-bar>
```
> Vue2
> Vue3
```
html
<!-- Vue2 支持的用法 -->
<uni-nav-bar>
<view
slot=
"left"
class=
"city"
>
```
html
<!-- Vue3 支持的用法 -->
<uni-nav-bar>
<template
v-slot:left
>
<view
class=
"city"
>
<!-- ... -->
</view>
</uni-nav-bar>
```
> Vue3
```
html
<!-- Vue3 支持的用法 -->
<uni-nav-bar>
<template
v-slot:left
>
<view
class=
"city"
>
<!-- ... -->
</view>
</template>
</uni-nav-bar>
```
</template>
</uni-nav-bar>
```
:::
:::
-
从 Vue 3.0 开始,过滤器已删除,不再支持,建议用方法调用或计算属性替换它们。
[
更多
](
https://v3.cn.vuejs.org/guide/migration/filters.html#%E6%A6%82%E8%A7%88
)
## 不再支持过滤器
-
在 Vue3 中,处理 API
`Promise 化`
调用结果的方式不同于 Vue2。
[
更多
](
https://uniapp.dcloud.io/api/#api-promise-化
)
从 Vue 3.0 开始,过滤器已删除,不再支持,建议用方法调用或计算属性替换它们。
[
更多
](
https://v3.cn.vuejs.org/guide/migration/filters.html#%E6%A6%82%E8%A7%88
)
-
Vue3 中,调用成功会进入 then 方法,调用失败会进入 catch 方法
-
Vue2 中,调用无论成功还是失败,都会进入 then 方法,返回数据的第一个参数是错误对象,第二个参数是返回数据
## 在 Vue3 中,处理 API `Promise 化` 调用结果的方式不同于 Vue2。[更多](https://uniapp.dcloud.io/api/#api-promise-化)
::: preview
-
Vue3 中,调用成功会进入 then 方法,调用失败会进入 catch 方法
-
Vue2 中,调用无论成功还是失败,都会进入 then 方法,返回数据的第一个参数是错误对象,第二个参数是返回数据
> Vue2
::: preview
```js
// Vue 2 转 Vue 3, 在 main.js 中写入以下代码即可
function isPromise(obj) {
return (
!!obj &&
(typeof obj === "object" || typeof obj === "function") &&
typeof obj.then === "function"
);
}
> Vue2
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]);
}
});
```
js
// Vue 2 转 Vue 3, 在 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
]);
}
});
},
});
```
> Vue3
});
},
});
```
```js
// Vue 3 转 Vue 2, 在 main.js 中写入以下代码即可
function isPromise(obj) {
return (
!!obj &&
(typeof obj === "object" || typeof obj === "function") &&
typeof obj.then === "function"
);
}
> Vue3
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);
},
});
```
```
js
// Vue 3 转 Vue 2, 在 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
);
},
});
```
:::
:::
-
uni-app 生命周期钩子在 Vue3 组合式 API 中的使用方式如下:
##
uni-app 生命周期钩子在 Vue3 组合式 API 中的使用方式如下:
- 在 Vue3 组合式 API 中,也需要遵循 uni-app 生命周期钩子规范, 如 onLaunch 等应用生命周期仅可在 App.vue 中监听,使用中请注意生命周期钩子的适用范围。[查看全部生命周期钩子](https://uniapp.dcloud.net.cn/collocation/frame/lifecycle)
- 只能在 `<script setup>` 单文件语法糖或 `setup()` 方法中使用生命周期钩子,以 A 页面跳转 B 页面传递参数为例:
...
...
@@ -374,12 +377,12 @@
:::
-
在 Vue3 中,this 对象下的
`$mp`
调整为
`$scope`
##
在 Vue3 中,this 对象下的 `$mp` 调整为 `$scope`
-
在 Vue3 中,如果 nvue 使用了 Vuex 的相关 API,需要在 main.js 的 createApp 的返回值中 return 一下 Vuex 示例:
##
在 Vue3 中,如果 nvue 使用了 Vuex 的相关 API,需要在 main.js 的 createApp 的返回值中 return 一下 Vuex 示例:
```js
import Vuex from
'vuex'
import Vuex from
"vuex";
export function createApp() {
const app = createSSRApp(App);
app.use(store);
...
...
@@ -390,42 +393,76 @@
}
```
-
App,小程序端源码调试,需要在 vite.config.js 中主动开启 sourcemap
-
App,小程序端源码调试,需要在 vite.config.js 中主动开启 sourcemap
```js
import { defineConfig } from "vite";
import uni from "@dcloudio/vite-plugin-uni";
```
js
import
{
defineConfig
}
from
"
vite
"
;
import
uni
from
"
@dcloudio/vite-plugin-uni
"
;
/**
* @type {import('vite').UserConfig}
*/
/**
* @type {import('vite').UserConfig}
*/
export default defineConfig({
build: {
sourcemap: true,
},
export
default
defineConfig
({
build
:
{
sourcemap
:
true
,
},
plugins: [uni()],
});
```
plugins
:
[
uni
()],
});
```
-
在 vue3 的小程序平台中,监听原生的点击事件可以先使用 tap。(在 vue3 中,移除了.native 修饰符,所以编译器无法预知 click 是要触发原生事件,还是组件的自定义事件,故并未转换成小程序的 tap 事件)
-
vue3 支持的手机版本最低到多少?
> vue3 支持的范围是:Android > 4.4, ios >= 10
-
vue3 nvue 暂不支持 recycle-list 组件
-
vue3 在 h5 平台发行时,为了优化包体积大小,会默认启动摇树,仅打包明确使用的api。如果要关闭摇树,可以在manifest.json中配置:
```
json
"h5"
:
{
"optimization"
:
{
"treeShaking"
:
{
"enable"
:
false
}
}
}
```
-
在 vue3 的小程序平台中,监听原生的点击事件可以先使用 tap。(在 vue3 中,移除了.native 修饰符,所以编译器无法预知 click 是要触发原生事件,还是组件的自定义事件,故并未转换成小程序的 tap 事件)
-
vue3 支持的手机版本最低到多少?
> vue3 支持的范围是:Android > 4.4, ios >= 10
-
vue3 nvue 暂不支持 recycle-list 组件
## [通过定义props来直接接收url传入的参数](#url-search-params)
\ No newline at end of file
-
vue3 在 h5 平台发行时,为了优化包体积大小,会默认启动摇树,仅打包明确使用的 api。如果要关闭摇树,可以在 manifest.json 中配置:
```
json
"h5"
:
{
"optimization"
:
{
"treeShaking"
:
{
"enable"
:
false
}
}
}
```
## url-search-params
通过定义 props 来直接接收 url 传入的参数
<!--方式1-->
```
html
<script
setup
>
// 页面可以通过定义 props 来直接接收 url 传入的参数
// 如:uni.navigateTo({ url: '/pages/index/index?id=10' })
const
props
=
defineProps
({
id
:
String
,
});
console
.
log
(
"
id=
"
+
props
.
id
);
// id=10
</script>
```
<!--方式2-->
```
html
<script>
// 页面可以通过定义 props 来直接接收 url 传入的参数
// 如:uni.navigateTo({ url: '/pages/index/index?id=10' })
export
default
{
props
:
{
id
:
{
type
:
String
,
},
},
setup
(
props
)
{
console
.
log
(
"
id=
"
+
props
.
id
);
// id=10
},
};
</script>
```
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录