就是一个组件,我只想全局注册一次,不需要在需要用的页面再去引用,像vue里面app.vue的那种效果
- 发布:2019-05-08 18:58
- 更新:2024-11-01 17:10
- 阅读:44210
最佳回复
DCloud_UNI_OttoJi - 日常回复 uni-app/x 问题,如果艾特我没看到,请主动私信
久等了,我来看下这个问题,我看到评论中提到的一些解决方案有平台限制,我研究下,有进展我会更新到这里。
多端通用的 uniapp 怎么设置共用的组件
uniapp 怎么设置所有页面共用的组件
https://ask.dcloud.net.cn/question/70510
我看了评论区各位的反馈,有的讨论已经大大拓展了最初的问题,也有热情的社区用户贡献了技术方案,特别感谢他们,为他们点赞。
很多人争论的点,是使用场景不同,小程序、app 环境会有一些限制,不能直接使用浏览器 h5 的方案,这样导致了一些用户评论的误解。我整理下不同的业务场景,供不同需求的人使用。下面场景始终考虑多端。
前置共识:
- 在小程序运行环境和 h5 不同,没有 document 方法,也就不能创建元素、插入元素。
- 小程序环境中,彼此页面独立
- uni-app 在 vue2 中使用 webpack 工具,在 vue3 中使用 vite 工具。
场景 1: 最简单的组件复用
无论是 h5、小程序、app 都遵循 vue 的写法,引入组件 - 使用组件。
也就是组件需要先引用注册,再去使用。在 vue2/vue3 中引入组件都一致。
场景 2:组件不想每次都 import 引入
组件每次都需要 import,推荐使用 easycom 功能。
easycom 是 HBuiderX 2.5.5 可用的功能,早期的讨论可能没注意到
使用 easycom 自动注册方案,使用 uni-app
提供的功能特性,自动按照规则自动匹配,直接使用
https://uniapp.dcloud.net.cn/collocation/pages.html#easycom
省去了导入的方案,效果和场景 1 一样,正常使用组件。
场景 3:想自定义实现 uni.showToast
uni-app 内置的 uni-showToast 可以通过函数调用实现消息弹窗,这个在小程序端是依赖小程序本身提供的功能做的包装。比如微信小程序提供了 wx.showToast
方法。
有的业务想拓展功能、更改样式,符合自己的业务逻辑。比如评论里提到的全局剪切板弹窗、全局悬浮球等。在 web/app 中很容易实现,但在小程序端不能实现,因为小程序本身不支持动态创建。那要怎么做呢?看看小程序组件库就知道了。
在小程序组件库中,为了解决这个场景,一般需要在 template 里使用组件,在逻辑中控制组件显隐,说到底是要保证弹窗里的元素在页面中存在,等待唤起生效。
伪代码如下:
<xx-popup show="{{ show }}" bind:close="onClose">内容</xx-popup>
如果不想在模版中声明组件,可以考虑在编译时候自动填充内容。举例子,比如用户这样伪代码:
<template>
<view>123</view>
</template>
通过编译器插件编译时候追加元素。
<template>
<my-toast ref='mytoast' />
<view>123</view>
</template>
也就是说,这个组件声明是必须存在的,只是由谁来完成。
在 uni-app 的工具链中,使用了 webpack/vite,社区热心的用户已经开发了对应的插件功能。
比如:
- webpack 方案
vue-inset-loader
,核心思路是调整 webpack 配置使用 loader,对文件进行中间处理。文档地址 https://ask.dcloud.net.cn/article/39345 仓库地址 https://github.com/1977474741/vue-inset-loader - vite 插件方案
vue3-inset-loader
,和上面思路一致,vite 方向的插件,仓库地址 https://github.com/smartXJ/vue3-inset-loader/tree/main
两个方案,都使用了相同的配置,不需要额外学习配置方法。由衷感谢社区的共享方案,大家有时间可以给点点 github star。
场景 4:想实现 app.vue 的效果
这里讨论比较多,用户想和 web 一样,对 app.vue 进行修改,实现一劳永逸的效果。如果考虑多端实现,就不能这样做。因为 app.vue 在小程序端不参与页面渲染。app.vue 地址
技术实现思路是,使用一个自定义组件放在页面顶部,通过插槽 slot 编写具体的页面逻辑,可以实现整体页面的包装。这种方案比较直观,同样可以实现场景 3 的需求。
<template>
<my-page>
<view>123</view>
</my-page>
</template>
这里推荐社区热心用户 自学的烦恼,提供的方案 https://ext.dcloud.net.cn/plugin?id=2560
总结
可以看到,最终还是因为平台限制不能想 web 一样灵活,产生了各种变通方案。
如果上面提到的场景,还不能满足你的需求和使用场景,你可以提供你的发行方案,描述使用场景反馈给我。
-
这个可以解决返回页面失效问题,代码有点复杂 可以适当理解一下 大致逻辑就是在全局定义的时候是注册在当前页面下而不是全局,调用的时候根据当前页面进行查找返回,这样其实每一个页面的show方法其实都在$w下
import Vue from 'vue'
export const name = 'gobalComponents'
// 配置全局组件,保证全局组件在返回页面时依旧可以使用
const get = (name) => {
let $w = Vue.prototype.$w
const page = uni.$u.page()
return $w?.gobalComponents[`${page}`][`${name}`]
}
const add = (name, fn) => {
let $w = Vue.prototype.$w
const page = uni.$u.page()
const addObj = ($w?.gobalComponents && $w?.gobalComponents[`${page}`]) ?
$w.gobalComponents[`${page}`] : {}
addObj[`${name}`] = fn
$w.gobalComponents = {
...$w.gobalComponents,
[`${page}`]: addObj
}
Vue.prototype.$w = {
...$w,
get [`${name}`]() {
return get(name)
}
}
}
export default {
add,
get
}
2022-09-03 22:00
-
回复 w***@126.com: 这里有一个小问题,由于用的是拓展运算符,所有如果又多个全局组件,最后一个会把前面的覆盖掉(也不算是覆盖 就是会让前面的get失效),后来考虑使用Proxy统一写一个get函数进行处理就ok了
2022-09-03 23:07
hhyang - 如有问题,请添加QQ1606726660 备注付费咨询
-
回复 hhyang: 他的意思是每个页面都要引入那个组件的插槽,他想要页面不写插槽,统一引入,所有页面公用,比如100个页面我要写100个插槽,但我就只想写一次
2021-02-22 17:17
-
回复 hhyang: 这么简单的问题都没有听懂,他的意思就是不用每个页面都有代码,在全局文件引入就行了,看你也不聪明到哪里去,一遇到这种问题,不理解就说这么极端的话,看不起人的最让人看不起。鄙视你这种回答问题的方式。自己250,别把所有人想成250
2021-04-10 14:41
-
回复 v***@163.com: 我也有这个需求,已经差不多快成熟的产品,现在要追加一个全局功能,要是每个页面再去加组件感觉不太好,而且万一以后还要加其他的就更麻烦了,不知道有解决办法没有
2021-12-23 10:44
楼主说的应该是Vue中app.vue文件里写的html内容吧?
是不是例如
<template>
<div id="app">
<router-view/>
<my-component></my-component>
</div>
</template>
中的my-component?
是的话我也想问这个。
否则页面多到十几或者几十个的话,每个页面都要去写一遍,太麻烦了。
现在uniapp里的app.vue没有html部分...
app平台,可以通过创建一个透明的页面A,将弹窗视图放到A页面上,显示弹窗就navigateto到A页面,具体实现可以参照这个示例 https://ext.dcloud.net.cn/plugin?id=6644
尹成诺 - 辣鸡前端
我也遇到了,不过也算是解决了。这里是思路:
https://ext.dcloud.net.cn/plugin?id=1294
vue-template-compiler 还能这么用
跨端通用参考方案:
- 新建一个 base-page.vue 文件作为页面根组件,其中引入公共组件,并暴露操作方法 showPopup。
- 新建一个 base-page.js 文件作为页面 mixin 引入根组件并暴露根组件操作方法 showPopup。
- 每个页面引入 base-page.js 并加入 mixins 中,根节点使用 base-page 组件。
- 页面或者组件中调用 this.$root.showPopup()。
扩展阅读:
- 具体实现可以参考其他 自学的烦恼 写的示例:https://ext.dcloud.net.cn/plugin?id=2560
- 其中重复的工作在步骤3,如想省略步骤3,可以尝试自行调整编译器,自动插入到每个页面。
神的尾巴 - 欢迎关注我的微信公众号:神的尾巴
我提供一种我目前的解决思路:
前面回答里面说的在App.vue里面,添加template,在APP实测无效
目前我有一个通用的page组件,用来处理作为所有页面的父组件,处理loading、骨架图等,在该父组件添加全局组件
获取ref,有两个方案:
- 在page组件添加方法getRef(),获取page组件内的ref
- 初始化的时候注册到vuex,在使用的时候到vuex中取
两种方案都能获取到,看你怎么使用方便,我是使用的方案2,然后在通用mixins中添加获取全局组件的方法
-
回复 s***@aliyun.com:
<template>
<view>
<router-view/>
<!-- 你的全局popup组件,在渲染成功后,没执行组件的mounted -->
<popup />
</view>
</template>2020-06-18 22:48
-
"目前我有一个通用的page组件,用来处理作为所有页面的父组件,处理loading、骨架图等,在该父组件添加全局组件",这句话不太明白,能多说点嘛?谢谢~
2020-07-16 10:33
简单得很,在components下创建组件目录,命名跟组件文件一致,然后把该组件放到对应的目录下,就可以直接在页面中使用了,不需要引入。同理,有多少个组件就创建多少个目录,记住:目录命名一定要跟组件命名一致
以下在App.vue中修改 h5可以正常使用,其他平台没有试过,而且App.vue中不能写v-model,只能用:value代替
<template>
<view>
<router-view />
<u-popup :value="true">哈哈</u-popup>
</view>
</template>
<script>
import uPopup from "@/uview-ui/components/u-popup/u-popup.vue";
export default {
components: {
'u-popup': uPopup
},
onLaunch: function() {
console.log('App Launch')
},
onShow: function() {
console.log('App Show');
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style lang="scss">
@import "uview-ui/index.scss";
</style>
借鉴 > https://ext.dcloud.net.cn/plugin?id=2560 也就这样比较好了。的方法
在App.vue中引用全局组件
App.vue代码如下:
<template>
<globe-page></globe-page>
</template>
<script>
import globePage from "@/components/globe-page.vue";
export default {
components: {
'globe-page': globePage
},
onLaunch: function() {
console.log('App Launch')
},
onShow: function() {
console.log('App Show');
},
onHide: function() {
console.log('App Hide')
}
}
</script>
globe-page.vue代码如下:
<template>
<view>
<router-view />
<u-popup ref="popup" v-model="show">哈哈</u-popup>
</view>
</template>
<script>
import uPopup from "@/uview-ui/components/u-popup/u-popup.vue";
export default {
components: {
'u-popup': uPopup
},
name:"globe-page",
data() {
return {
show: false
};
}
}
</script>
<style>
</style>
关于如何在App.vue 的中onShow中改变 globe-page.vue中的 show,有多种方法可以实现,这里就不说了。
兄弟们,可以参考,这个,全平台可用
-
大佬,按这个方法修改后会报错,无法正常编译到小程序,不知道哪里有问题,具体错误如下:
[HBuilder] 19:02:12.579 ERROR Error: EINVAL: invalid argument, mkdir 'E:\uniAppProject\MyTest\unpackage\dist\build\mp-weixin\E:\uniAppProject\MyTest\components\test'
[HBuilder] 19:02:12.579 Error: EINVAL: invalid argument, mkdir 'E:\uniAppProject\MyTest\unpackage\dist\build\mp-weixin\E:\uniAppProject\MyTest\components\test'2023-07-18 19:06
董路飞 - 搜索小程序麻丝和 App 麻丝
此贴发布:【2019-05-08 18:58】
现在这个时间 【2023/08/28】,
只需要在根目录创建components文件夹,里面写要复用的组件,然后其他页面就可以直接使用这个组件了(不需要在项目配置文件里写配置,也不需要在页面文件引入此组件)
这不就相当于公共组件?
s***@outlook.com - rookie
2024年了还没一个官方的解决方案吗?我需要一个无网络的提示页面(注意是页面不是弹框),需要所有页面都可以使用,但是我现在有100个页面,能有方案只需要引入一次就可以全局使用吗?总不能我写一个组件,100个页面全部去引入一遍吧.
锦鲤丶接单丶 - 锦鲤丶接单丶18560000860丶10+年开发经验
我这边变相的算是实现了,就是自定义一个根组件,然后每个页面的第一级组件就放这个根组件,在根组件中放公共的header、footer、公共组件等元素、然后在写个默认的<slot> 来接收每个页面的内容
你算哪根聪 - !!!
正好最近用 uni-app 做 app 端(H5 的小伙伴们可以走了),大概 OP 的需求跟我差不多,说多了都是空话,上代码(代码不用全看完,提供思路最重要)
1.调用代码
this.$showModal({
success: () => {
console.log('success');
}
})
- 新建一个 nvue 的页面(我就是复制粘贴,有些 css 引用就不要了,你们就当伪代码看):
<template>
<view class="confirm-modal">
<view class="_confirm-modal" :style="{
padding: `${r32}px`,
borderRadius: `${r16}px`
}" @tap.stop>
<text class="_confirm-modal-title" :style="{
fontSize: `${r28}px`,
lineHeight: `${r32}px`
}">{{ title }}</text>
<text class="_confirm-modal-text" :style="{
fontSize: `${r26}px`,
lineHeight: `${r30}px`,
margin: `${r32}px 0 ${r48}px 0`
}">{{ text }}</text>
<view class="_confirm-modal-button">
<text class="_confirm-modal-ok" @click="handleConfirm()" :style="{
width: `${r260}px`,
height: `${r80}px`,
lineHeight: `${r80}px`,
fontSize: `${r26}px`,
borderRadius: `${r40}px`
}">{{ confirmText }}</text>
<text class="_confirm-modal-cancel" @click="handleCancel()" :style="{
width: `${r260}px`,
height: `${r78}px`,
lineHeight: `${r78}px`,
fontSize: `${r26}px`,
borderRadius: `${r40}px`,
marginLeft: `${r16}px`,
border: `${r1}px solid #999999`
}">{{ cancelText }}</text>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
title: '提示',
text: '提示内容',
confirmText: '确认',
cancelText: '取消',
r1: uni.upx2px(1),
r16: uni.upx2px(16),
r26: uni.upx2px(26),
r28: uni.upx2px(28),
r30: uni.upx2px(30),
r32: uni.upx2px(32),
r40: uni.upx2px(40),
r48: uni.upx2px(48),
r78: uni.upx2px(78),
r80: uni.upx2px(80),
r260: uni.upx2px(260),
}
},
beforeCreate() {
const domModule = uni.requireNativePlugin('dom')
domModule.addRule('fontFace', {
'fontFamily': "PingFang-SC-Medium",
'src': "url('/static/fonts/PingFang-SC-Medium.ttf')"
});
},
methods: {
handleConfirm() {
uni.$emit('handleModalConfirm')
},
handleCancel() {
uni.$emit('handleModalCancel')
}
}
}
</script>
<style lang="scss" scoped>
.confirm-modal {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.3);
._confirm-modal {
background-color: $white;
._confirm-modal-title {
color: $black;
text-align: center;
font-weight: 500;
font-family: $font-m;
}
._confirm-modal-text {
color: #757575;
text-align: center;
}
._confirm-modal-button {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
._confirm-modal-ok {
text-align: center;
display: flex;
justify-content: center;
align-items: center;
color: $white;
font-weight: 500;
font-family: $font-m;
background-color: $p-color;
}
._confirm-modal-cancel {
text-align: center;
display: flex;
justify-content: center;
align-items: center;
color: $p-color12;
font-weight: 500;
font-family: $font-m;
background-color: $white;
}
}
}
}
</style>
3.把这个弹窗的 nvue 页面注册到 pages.json,然后再把页面路径注册到 subNvues:
"subNVues": [{
"id": "bModal",
"path": "pages/modal/modal",
"type": "popup",
"style": {
"background": "transparent"
}
}]
4.就是在 App.vue 里面注册 this.$showModal
<script>
import {
mapGetters
} from 'vuex'
let modalInstance = null
export default {
onLaunch: function() {
console.log('App Launch')
this.initModal()
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
},
computed: {
...mapGetters(['showModal'])
},
watch: {
showModal: {
handler(val) {
if (!modalInstance) {
modalInstance = uni.getSubNVueById('bModal')
}
if (val) {
modalInstance.show()
} else {
modalInstance.hide()
}
},
deep: true
}
},
methods: {
// 基础弹窗
initModal() {
let modalSuccess = null
uni.$on('handleModalConfirm', () => {
if (modalSuccess) {
modalSuccess()
}
})
uni.$on('handleModalCancel', () => {
this.$store.commit('SET_SHOW_MODAL', false)
})
Vue.prototype.$showModal = (({ success }) => {
modalSuccess = success
this.$store.commit('SET_SHOW_MODAL', true)
})
}
}
}
</script>
<style lang="scss">
/*每个页面公共css */
/* 禁用网页默认的橡皮筋效果 */
* {
overscroll-behavior: none;
}
/* 注意要写在第一行,同时给style标签加入lang="scss"属性 */
@import "@/uni_modules/uview-ui/index.scss";
</style>
1***@qq.com
希望能支持小程序,app(vue,nvue) 这些页面,谢谢
2024-01-30 11:16
DCloud_UNI_OttoJi
回复 1***@qq.com: 请看下我楼上的回复,是否满足你的要求,解答你的疑问。如果不能,请补充你的具体使用场景。
2024-01-31 15:30
1***@qq.com
回复 DCloud_UNI_OttoJi: 之前开发的 app 场景就是之前页面开发太多了,逻辑比较多,不想全部改动变成插槽的形式,有没有其他方式实现呢
2024-04-12 17:22
爱tutu爱生活
不能解决要求啊,app从登录到使用的页面如果断网了要像微信那种的提示如何实现?
2024-06-19 15:52
_Skiyee
大家可以看看这个解决方案,模拟实现 app.vue 的效果,目前Hbx与cli项目均支持
https://github.com/uni-ku/root
2024-09-10 17:32